loading
Generated 2020-10-19T12:54:10-04:00

All Files ( 75.59% covered at 1781.46 hits/line )

653 files in total.
19417 relevant lines, 14677 lines covered and 4740 lines missed. ( 75.59% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card-mod-bootstrap/lib/bootstrap.rb 90.00 % 16 10 9 1 1.20
card-mod-bootstrap/lib/bootstrap/basic_tags.rb 40.00 % 26 15 6 9 0.40
card-mod-bootstrap/lib/bootstrap/component.rb 33.03 % 197 109 36 73 0.54
card-mod-bootstrap/lib/bootstrap/component/carousel.rb 27.50 % 68 40 11 29 0.28
card-mod-bootstrap/lib/bootstrap/component/form.rb 41.67 % 65 24 10 14 0.96
card-mod-bootstrap/lib/bootstrap/component/horizontal_form.rb 44.83 % 63 29 13 16 0.45
card-mod-bootstrap/lib/bootstrap/component/layout.rb 85.00 % 95 40 34 6 2.50
card-mod-bootstrap/lib/bootstrap/component/panel.rb 100.00 % 9 6 6 0 1.00
card-mod-bootstrap/lib/bootstrap/component_loader.rb 100.00 % 28 16 16 0 3.25
card-mod-bootstrap/lib/bootstrap/delegate.rb 75.00 % 16 8 6 2 27.50
card-mod-bootstrap/lib/bootstrap/old_component.rb 81.00 % 182 100 81 19 8.20
card-mod-bootstrap/lib/bootstrapper.rb 88.89 % 16 9 8 1 1.89
card-mod-bootstrap/lib/card/lazy_tab.rb 95.00 % 37 20 19 1 3.05
card-mod-bootstrap/lib/card/tab.rb 97.50 % 76 40 39 1 3.18
card-mod-carrierwave/lib/carrier_wave/card_mount.rb 100.00 % 134 12 12 0 1.42
card-mod-carrierwave/lib/carrier_wave/file_card_uploader.rb 89.77 % 320 88 79 9 653.31
card-mod-carrierwave/lib/carrier_wave/image_card_uploader.rb 100.00 % 59 22 22 0 343.09
card-mod-delayed_job/lib/card/mod/delayed_job.rb 100.00 % 1 1 1 0 1.00
card-mod-delayed_job/lib/cardio/delayed_job.rb 100.00 % 9 5 5 0 7.80
card-mod-email/format/email_html_format.rb 83.33 % 14 6 5 1 0.83
card-mod-email/format/email_text_format.rb 100.00 % 12 5 5 0 10.40
card-mod-follow/lib/card/follow_option.rb 80.00 % 33 15 12 3 1.27
card-mod-follow/lib/card/follower_stash.rb 97.83 % 88 46 45 1 62.20
card-mod-format/format/css_format.rb 75.00 % 17 8 6 2 0.75
card-mod-format/format/csv_format.rb 75.00 % 19 8 6 2 0.75
card-mod-format/format/file_format.rb 100.00 % 8 3 3 0 1.00
card-mod-format/format/js_format.rb 75.00 % 17 8 6 2 0.75
card-mod-format/format/json_format.rb 63.64 % 24 11 7 4 1.55
card-mod-format/format/rss_format.rb 100.00 % 9 4 4 0 1.00
card-mod-format/format/xml_format.rb 83.33 % 13 6 5 1 0.83
card-mod-history/lib/card/act.rb 75.00 % 137 44 33 11 31.68
card-mod-history/lib/card/act/act_renderer.rb 89.89 % 217 89 80 9 2.76
card-mod-history/lib/card/act/act_renderer/absolute_act_renderer.rb 100.00 % 34 13 13 0 1.38
card-mod-history/lib/card/action.rb 91.14 % 230 79 72 7 80.05
card-mod-history/lib/card/action/action_renderer.rb 87.23 % 94 47 41 6 2.26
card-mod-history/lib/card/action/admin.rb 31.58 % 36 19 6 13 0.32
card-mod-history/lib/card/action/differ.rb 82.50 % 89 40 33 7 4.98
card-mod-history/lib/card/change.rb 77.78 % 70 18 14 4 23.50
card-mod-machines/lib/card/machine.rb 57.89 % 33 19 11 8 65.79
card-mod-platypus/lib/card/mod/platypus.rb 100.00 % 1 1 1 0 1.00
card/lib/application_job.rb 100.00 % 2 1 1 0 1.00
card/lib/application_record.rb 100.00 % 3 2 2 0 1.00
card/lib/card.rb 100.00 % 157 20 20 0 33.25
card/lib/card/auth.rb 88.89 % 45 18 16 2 20.50
card/lib/card/auth/current.rb 90.41 % 158 73 66 7 455.34
card/lib/card/auth/permissions.rb 96.88 % 72 32 31 1 1237.16
card/lib/card/auth/proxy.rb 96.30 % 59 27 26 1 1923.44
card/lib/card/auth/setup.rb 89.29 % 55 28 25 3 7.68
card/lib/card/auth/token.rb 94.74 % 38 19 18 1 1.89
card/lib/card/cache.rb 77.33 % 181 75 58 17 26580.75
card/lib/card/cache/persistent.rb 89.06 % 154 64 57 7 3383.67
card/lib/card/cache/prepopulate.rb 45.00 % 48 20 9 11 0.45
card/lib/card/cache/temporary.rb 86.36 % 58 22 19 3 23976.95
card/lib/card/codename.rb 88.33 % 153 60 53 7 5431.45
card/lib/card/content.rb 86.11 % 153 72 62 10 873.57
card/lib/card/content/chunk.rb 95.65 % 96 46 44 2 724.80
card/lib/card/content/chunk/abstract.rb 92.86 % 88 42 39 3 2226.74
card/lib/card/content/clean.rb 76.00 % 95 50 38 12 45.92
card/lib/card/content/diff.rb 78.79 % 70 33 26 7 1.67
card/lib/card/content/diff/l_c_s.rb 75.51 % 90 49 37 12 1.80
card/lib/card/content/diff/l_c_s/processor.rb 80.65 % 163 93 75 18 3.17
card/lib/card/content/diff/result.rb 69.15 % 159 94 65 29 2.05
card/lib/card/content/parser.rb 93.88 % 104 49 46 3 3416.55
card/lib/card/content/truncate.rb 26.32 % 67 38 10 28 0.26
card/lib/card/director.rb 90.48 % 231 63 57 6 286.81
card/lib/card/director/act_direction.rb 91.80 % 111 61 56 5 264.72
card/lib/card/director/card_methods.rb 100.00 % 45 23 23 0 610.22
card/lib/card/director/event_delay.rb 100.00 % 44 19 19 0 31.79
card/lib/card/director/phases.rb 100.00 % 55 32 32 0 161.13
card/lib/card/director/run.rb 93.85 % 118 65 61 4 1365.48
card/lib/card/director/stages.rb 79.55 % 85 44 35 9 2652.36
card/lib/card/director/store.rb 100.00 % 72 33 33 0 122.45
card/lib/card/director/subdirector_array.rb 100.00 % 51 29 29 0 203.59
card/lib/card/dirty.rb 100.00 % 70 34 34 0 1423.15
card/lib/card/dirty/method_factory.rb 100.00 % 15 8 8 0 1630.25
card/lib/card/env.rb 83.93 % 97 56 47 9 1821.61
card/lib/card/env/location.rb 72.73 % 44 22 16 6 1125.45
card/lib/card/env/location_history.rb 96.43 % 54 28 27 1 105.04
card/lib/card/env/request_assignments.rb 100.00 % 24 12 12 0 148.67
card/lib/card/env/serialization.rb 100.00 % 24 12 12 0 147.25
card/lib/card/env/slot_options.rb 100.00 % 30 17 17 0 187.24
card/lib/card/env/success.rb 84.78 % 171 92 78 14 85.22
card/lib/card/error.rb 81.18 % 173 85 69 16 3.38
card/lib/card/format.rb 85.71 % 94 49 42 7 2059.02
card/lib/card/format/content.rb 92.86 % 89 42 39 3 2266.62
card/lib/card/format/context_names.rb 89.66 % 57 29 26 3 935.14
card/lib/card/format/error.rb 52.17 % 80 46 24 22 1306.04
card/lib/card/format/method_delegation.rb 100.00 % 71 33 33 0 29670.48
card/lib/card/format/nest.rb 100.00 % 81 39 39 0 3463.62
card/lib/card/format/nest/fetch.rb 85.37 % 75 41 35 6 1874.71
card/lib/card/format/nesting.rb 75.68 % 74 37 28 9 1498.30
card/lib/card/format/nesting/main.rb 66.67 % 48 24 16 8 191.13
card/lib/card/format/nesting/mode.rb 81.25 % 88 32 26 6 1455.13
card/lib/card/format/nesting/subformat.rb 81.82 % 64 33 27 6 2601.70
card/lib/card/format/registration.rb 100.00 % 53 31 31 0 2324.29
card/lib/card/format/render.rb 67.53 % 148 77 52 25 9097.56
card/lib/card/format/wrapper.rb 87.50 % 49 24 21 3 3856.50
card/lib/card/lexicon.rb 95.24 % 90 42 40 2 13896.19
card/lib/card/mailer.rb 100.00 % 38 18 18 0 8.72
card/lib/card/mark.rb 72.41 % 67 29 21 8 66023.34
card/lib/card/mod.rb 94.74 % 95 19 18 1 119.42
card/lib/card/mod/dirs.rb 90.91 % 181 77 70 7 815.38
card/lib/card/mod/load_strategy.rb 76.92 % 72 39 30 9 35.08
card/lib/card/mod/load_strategy/pattern_tmp_files.rb 100.00 % 29 17 17 0 3.41
card/lib/card/mod/load_strategy/set_tmp_files.rb 100.00 % 34 19 19 0 124.74
card/lib/card/mod/load_strategy/tmp_files.rb 100.00 % 43 23 23 0 115.17
card/lib/card/mod/loader.rb 80.95 % 94 42 34 8 24.45
card/lib/card/mod/loader/set_loader.rb 91.25 % 159 80 73 7 190.36
card/lib/card/mod/loader/set_pattern_loader.rb 80.00 % 59 25 20 5 2.08
card/lib/card/mod/module_template.rb 97.30 % 83 37 36 1 267.08
card/lib/card/name.rb 93.18 % 87 44 41 3 86645.16
card/lib/card/name/fields_and_traits.rb 96.55 % 64 29 28 1 683.45
card/lib/card/name/name_variants.rb 58.82 % 36 17 10 7 0.59
card/lib/card/query.rb 100.00 % 110 23 23 0 221.26
card/lib/card/query/abstract_query.rb 91.67 % 95 48 44 4 1168.29
card/lib/card/query/abstract_query/query_helper.rb 100.00 % 56 30 30 0 1127.77
card/lib/card/query/abstract_query/tie.rb 88.46 % 104 52 46 6 629.62
card/lib/card/query/act_query.rb 84.62 % 26 13 11 2 102.46
card/lib/card/query/action_query.rb 68.42 % 36 19 13 6 70.21
card/lib/card/query/card_query.rb 96.67 % 59 30 29 1 308.67
card/lib/card/query/card_query/conjunctions.rb 92.31 % 48 26 24 2 43.08
card/lib/card/query/card_query/found_by.rb 90.48 % 43 21 19 2 3.19
card/lib/card/query/card_query/interpretation.rb 75.51 % 100 49 37 12 503.69
card/lib/card/query/card_query/match_attributes.rb 85.00 % 44 20 17 3 2.55
card/lib/card/query/card_query/normalization.rb 87.88 % 60 33 29 4 621.12
card/lib/card/query/card_query/reference_attributes.rb 86.67 % 45 15 13 2 16.60
card/lib/card/query/card_query/relational_attributes.rb 77.78 % 95 45 35 10 26.02
card/lib/card/query/card_query/run.rb 81.67 % 112 60 49 11 244.33
card/lib/card/query/card_query/sorting.rb 32.65 % 101 49 16 33 0.33
card/lib/card/query/clause.rb 100.00 % 19 9 9 0 292.89
card/lib/card/query/join.rb 82.00 % 126 50 41 9 1149.86
card/lib/card/query/reference_query.rb 90.00 % 54 30 27 3 112.40
card/lib/card/query/sql_statement.rb 92.42 % 120 66 61 5 379.32
card/lib/card/query/sql_statement/joins.rb 97.30 % 62 37 36 1 854.11
card/lib/card/query/sql_statement/order.rb 90.00 % 68 30 27 3 346.40
card/lib/card/query/sql_statement/where.rb 87.04 % 102 54 47 7 711.54
card/lib/card/query/value.rb 88.68 % 94 53 47 6 284.64
card/lib/card/query/value/match_value.rb 96.77 % 65 31 30 1 5.68
card/lib/card/reference.rb 52.94 % 85 34 18 16 51.97
card/lib/card/set.rb 100.00 % 85 24 24 0 1.00
card/lib/card/set/abstract.rb 100.00 % 6 3 3 0 1.00
card/lib/card/set/advanced_api.rb 78.38 % 71 37 29 8 10.24
card/lib/card/set/basket.rb 89.47 % 59 19 17 2 65.74
card/lib/card/set/event.rb 98.25 % 177 57 56 1 912.77
card/lib/card/set/event/callbacks.rb 93.75 % 30 16 15 1 3004.69
card/lib/card/set/event/delayed_event.rb 76.12 % 145 67 51 16 78.28
card/lib/card/set/event/options.rb 92.86 % 80 42 39 3 119.52
card/lib/card/set/format.rb 97.78 % 152 45 44 1 2523.09
card/lib/card/set/format/abstract_format.rb 92.00 % 126 25 23 2 46.40
card/lib/card/set/format/abstract_format/haml_views.rb 80.00 % 44 15 12 3 3.00
card/lib/card/set/format/abstract_format/view_definition.rb 80.00 % 67 35 28 7 126.03
card/lib/card/set/format/abstract_format/view_opts.rb 97.06 % 68 34 33 1 788.71
card/lib/card/set/format/abstract_format/wrapper.rb 96.77 % 91 31 30 1 96.55
card/lib/card/set/format/haml_paths.rb 82.00 % 85 50 41 9 737.78
card/lib/card/set/helpers.rb 95.65 % 44 23 22 1 2528.70
card/lib/card/set/i18n_scope.rb 90.00 % 111 40 36 4 42.38
card/lib/card/set/inheritance.rb 93.10 % 87 29 27 2 167.97
card/lib/card/set/loader.rb 100.00 % 72 26 26 0 138.96
card/lib/card/set/pattern.rb 48.15 % 47 27 13 14 308.15
card/lib/card/set/pattern/base.rb 95.24 % 157 84 80 4 18678.56
card/lib/card/set/required_field.rb 85.00 % 113 60 51 9 1.30
card/lib/card/set/trait.rb 88.89 % 77 45 40 5 41.36
card/lib/card/set/type.rb 100.00 % 46 26 26 0 2950.08
card/lib/card/subcards.rb 78.57 % 114 56 44 12 257.75
card/lib/card/subcards/add.rb 80.00 % 120 60 48 12 134.20
card/lib/card/subcards/relate.rb 78.57 % 27 14 11 3 52.93
card/lib/card/subcards/remove.rb 66.67 % 44 27 18 9 8.33
card/lib/card/version.rb 100.00 % 11 5 5 0 45.60
card/lib/card/view.rb 84.09 % 119 44 37 7 13257.84
card/lib/card/view/cache.rb 48.21 % 298 56 27 29 780.73
card/lib/card/view/cache/cache_action.rb 61.36 % 121 44 27 17 3972.80
card/lib/card/view/cache/stub.rb 60.00 % 46 15 9 6 0.60
card/lib/card/view/classy.rb 76.81 % 168 69 53 16 9807.33
card/lib/card/view/options.rb 66.67 % 93 15 10 5 0.67
card/lib/card/view/options/key_lists.rb 83.33 % 45 18 15 3 5307.78
card/lib/card/view/options/visibility.rb 93.18 % 99 44 41 3 12851.25
card/lib/card/view/options/voo_api.rb 92.22 % 204 90 83 7 22599.60
card/lib/card/view/permission.rb 100.00 % 54 25 25 0 8687.32
card/mod/core/chunk/escaped_literal.rb 100.00 % 27 10 10 0 2.40
card/mod/core/chunk/keep_escaped_literal.rb 100.00 % 26 10 10 0 2.40
card/mod/core/chunk/link.rb 75.81 % 121 62 47 15 678.23
card/mod/core/chunk/nest.rb 81.82 % 127 66 54 12 1493.77
card/mod/core/chunk/query_reference.rb 85.19 % 95 27 23 4 17.19
card/mod/core/chunk/reference.rb 73.33 % 59 30 22 8 1586.03
card/mod/core/chunk/uri.rb 86.79 % 145 53 46 7 2.66
card/mod/core/chunk/view_stub.rb 48.39 % 62 31 15 16 0.48
card/mod/core/format/data_format.rb 100.00 % 6 3 3 0 1.00
card/mod/core/format/html_format.rb 100.00 % 45 20 20 0 3412.00
card/mod/core/format/text_format.rb 87.50 % 16 8 7 1 0.88
card/mod/core/lib/card/fetch.rb 96.77 % 54 31 30 1 71676.81
card/mod/core/lib/card/fetch/results.rb 97.30 % 125 74 72 2 7958.54
card/mod/core/lib/card/fetch/retrieve.rb 100.00 % 56 30 30 0 30493.50
card/mod/core/lib/card/fetch/store.rb 100.00 % 66 31 31 0 39290.42
card/mod/core/lib/card/rule.rb 93.10 % 60 29 27 2 890.90
card/mod/core/lib/card/rule/cache.rb 100.00 % 69 29 29 0 6734.48
card/mod/core/lib/card/rule/preference_cache.rb 100.00 % 74 30 30 0 191.87
card/mod/core/lib/card/rule/read_rule_cache.rb 100.00 % 30 11 11 0 139.73
card/mod/settings/lib/card/setting.rb 85.42 % 99 48 41 7 11.77
card/mod/standard/lib/card/layout.rb 76.92 % 93 52 40 12 40.50
card/mod/standard/lib/card/layout/card_layout.rb 78.26 % 44 23 18 5 20.52
card/tmpsets/set/mod001-utility/abstract/bs_badge.rb 56.25 % 31 16 9 7 0.75
card/tmpsets/set/mod001-utility/abstract/filterable.rb 53.85 % 22 13 7 6 0.77
card/tmpsets/set/mod001-utility/abstract/filterable_bar.rb 63.64 % 20 11 7 4 0.82
card/tmpsets/set/mod001-utility/abstract/utility.rb 60.00 % 22 10 6 4 0.80
card/tmpsets/set/mod002-admin/self/admin.rb 28.77 % 152 73 21 52 0.33
card/tmpsets/set/mod002-admin/self/admin_info.rb 52.00 % 57 25 13 12 0.64
card/tmpsets/set/mod002-admin/self/debugger.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod002-admin/self/trash.rb 48.00 % 72 25 12 13 0.60
card/tmpsets/set/mod002-admin/self/version.rb 100.00 % 22 8 8 0 57.00
card/tmpsets/set/mod003-core/abstract/code_file.rb 40.00 % 93 50 20 30 1.78
card/tmpsets/set/mod003-core/abstract/haml_file.rb 58.82 % 33 17 10 7 0.76
card/tmpsets/set/mod003-core/abstract/lock.rb 47.37 % 35 19 9 10 0.58
card/tmpsets/set/mod003-core/abstract/vendor_code_file.rb 90.00 % 19 10 9 1 1.10
card/tmpsets/set/mod003-core/all/abort.rb 79.55 % 85 44 35 9 145.02
card/tmpsets/set/mod003-core/all/actify.rb 90.70 % 77 43 39 4 226.98
card/tmpsets/set/mod003-core/all/active_card.rb 100.00 % 15 7 7 0 160.43
card/tmpsets/set/mod003-core/all/assign_attributes.rb 93.67 % 135 79 74 5 2948.99
card/tmpsets/set/mod003-core/all/cache.rb 82.09 % 118 67 55 12 6451.36
card/tmpsets/set/mod003-core/all/chunk.rb 87.95 % 151 83 73 10 39.99
card/tmpsets/set/mod003-core/all/codename.rb 66.67 % 38 21 14 7 206.62
card/tmpsets/set/mod003-core/all/collection.rb 63.64 % 64 33 21 12 63.03
card/tmpsets/set/mod003-core/all/content.rb 81.52 % 187 92 75 17 375.76
card/tmpsets/set/mod003-core/all/contextual_content.rb 100.00 % 37 20 20 0 775.80
card/tmpsets/set/mod003-core/all/debug.rb 52.63 % 32 19 10 9 2.74
card/tmpsets/set/mod003-core/all/event_conditions.rb 84.78 % 181 92 78 14 5271.79
card/tmpsets/set/mod003-core/all/export.rb 40.00 % 77 40 16 24 0.48
card/tmpsets/set/mod003-core/all/extended.rb 92.00 % 58 25 23 2 1.04
card/tmpsets/set/mod003-core/all/fetch.rb 77.55 % 131 49 38 11 9021.39
card/tmpsets/set/mod003-core/all/fetch_helper.rb 100.00 % 44 22 22 0 230.41
card/tmpsets/set/mod003-core/all/haml.rb 75.86 % 84 29 22 7 349.83
card/tmpsets/set/mod003-core/all/i18n.rb 100.00 % 19 9 9 0 5.89
card/tmpsets/set/mod003-core/all/initialize.rb 100.00 % 85 49 49 0 5077.78
card/tmpsets/set/mod003-core/all/item.rb 68.83 % 139 77 53 24 32.42
card/tmpsets/set/mod003-core/all/layouts.rb 64.71 % 43 17 11 6 0.82
card/tmpsets/set/mod003-core/all/location_history.rb 85.71 % 28 14 12 2 24.14
card/tmpsets/set/mod003-core/all/name.rb 80.45 % 248 133 107 26 3077.98
card/tmpsets/set/mod003-core/all/name_events.rb 67.11 % 143 76 51 25 51.91
card/tmpsets/set/mod003-core/all/observer.rb 100.00 % 31 16 16 0 69.13
card/tmpsets/set/mod003-core/all/pattern.rb 93.94 % 63 33 31 2 8850.76
card/tmpsets/set/mod003-core/all/permissions.rb 85.60 % 242 125 107 18 448.68
card/tmpsets/set/mod003-core/all/references.rb 78.49 % 200 93 73 20 172.18
card/tmpsets/set/mod003-core/all/rename.rb 33.33 % 42 27 9 18 0.41
card/tmpsets/set/mod003-core/all/rules.rb 82.61 % 90 46 38 8 6818.00
card/tmpsets/set/mod003-core/all/states.rb 85.71 % 93 35 30 5 7981.26
card/tmpsets/set/mod003-core/all/subcards.rb 68.66 % 128 67 46 21 166.01
card/tmpsets/set/mod003-core/all/tabs.rb 40.00 % 60 30 12 18 0.50
card/tmpsets/set/mod003-core/all/templating.rb 95.00 % 80 40 38 2 2509.18
card/tmpsets/set/mod003-core/all/trash.rb 55.07 % 136 69 38 31 9.81
card/tmpsets/set/mod003-core/all/type.rb 88.24 % 70 34 30 4 1334.74
card/tmpsets/set/mod003-core/all/update_read_rules.rb 100.00 % 21 9 9 0 54.78
card/tmpsets/set/mod003-core/all/utils.rb 31.48 % 101 54 17 37 0.37
card/tmpsets/set/mod004-card-mod-format/all/all_css.rb 54.17 % 46 24 13 11 0.67
card/tmpsets/set/mod004-card-mod-format/all/all_csv.rb 38.89 % 102 54 21 33 0.44
card/tmpsets/set/mod004-card-mod-format/all/all_js.rb 77.78 % 19 9 7 2 1.11
card/tmpsets/set/mod004-card-mod-format/all/base.rb 77.78 % 146 72 56 16 185.29
card/tmpsets/set/mod004-card-mod-format/all/file.rb 77.78 % 18 9 7 2 1.11
card/tmpsets/set/mod004-card-mod-format/all/head.rb 93.42 % 157 76 71 5 241.14
card/tmpsets/set/mod004-card-mod-format/all/json.rb 53.42 % 164 73 39 34 1.64
card/tmpsets/set/mod004-card-mod-format/all/rss.rb 45.83 % 85 48 22 26 0.52
card/tmpsets/set/mod004-card-mod-format/all/text.rb 100.00 % 16 7 7 0 8.14
card/tmpsets/set/mod004-card-mod-format/self/head.rb 91.67 % 31 12 11 1 56.92
card/tmpsets/set/mod004-card-mod-format/type/html.rb 83.33 % 36 18 15 3 65.56
card/tmpsets/set/mod004-card-mod-format/type/json.rb 70.83 % 50 24 17 7 3.21
card/tmpsets/set/mod004-card-mod-format/type/plain_text.rb 100.00 % 18 9 9 0 1.33
card/tmpsets/set/mod005-pointer/abstract/00_paging_params.rb 95.00 % 40 20 19 1 14.85
card/tmpsets/set/mod005-pointer/abstract/01_paging.rb 86.49 % 156 74 64 10 9.20
card/tmpsets/set/mod005-pointer/abstract/01_paging/paging_links.rb 100.00 % 98 43 43 0 3.74
card/tmpsets/set/mod005-pointer/abstract/02_items.rb 75.86 % 207 87 66 21 554.29
card/tmpsets/set/mod005-pointer/abstract/02_pointer.rb 90.00 % 19 10 9 1 1.50
card/tmpsets/set/mod005-pointer/abstract/02_pointer/events.rb 57.89 % 80 38 22 16 11.37
card/tmpsets/set/mod005-pointer/abstract/02_pointer/html_views.rb 65.57 % 134 61 40 21 5.25
card/tmpsets/set/mod005-pointer/abstract/02_pointer/html_views/filter.rb 46.88 % 71 32 15 17 0.66
card/tmpsets/set/mod005-pointer/abstract/02_pointer/options_api.rb 86.05 % 87 43 37 6 8.35
card/tmpsets/set/mod005-pointer/abstract/02_pointer/other_views.rb 62.00 % 111 50 31 19 1.70
card/tmpsets/set/mod005-pointer/abstract/code_pointer.rb 75.00 % 44 20 15 5 4.85
card/tmpsets/set/mod005-pointer/abstract/id_pointer.rb 50.00 % 30 14 7 7 0.64
card/tmpsets/set/mod005-pointer/right/content_options.rb 100.00 % 13 7 7 0 2.14
card/tmpsets/set/mod005-pointer/self/input_options.rb 92.86 % 22 14 13 1 1.07
card/tmpsets/set/mod005-pointer/self/script_editors.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/self/script_libraries.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/self/script_mods.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/self/script_pointer_config.rb 88.89 % 18 9 8 1 1.11
card/tmpsets/set/mod005-pointer/self/style_libraries.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/self/style_mods.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/type/link_list.rb 53.85 % 49 26 14 12 0.69
card/tmpsets/set/mod005-pointer/type/list.rb 76.92 % 25 13 10 3 41.46
card/tmpsets/set/mod005-pointer/type/mirror_list.rb 36.73 % 102 49 18 31 0.41
card/tmpsets/set/mod005-pointer/type/mirrored_list.rb 31.75 % 121 63 20 43 0.35
card/tmpsets/set/mod005-pointer/type/nest_list.rb 53.33 % 55 30 16 14 0.67
card/tmpsets/set/mod005-pointer/type/pointer.rb 70.00 % 19 10 7 3 1.00
card/tmpsets/set/mod006-card-mod-virtual/abstract/virtual_cache.rb 54.17 % 52 24 13 11 0.63
card/tmpsets/set/mod007-card-mod-machines/abstract/machine.rb 45.05 % 230 111 50 61 16.98
card/tmpsets/set/mod007-card-mod-machines/abstract/machine/output_cache.rb 50.00 % 25 12 6 6 0.75
card/tmpsets/set/mod007-card-mod-machines/abstract/machine/output_update.rb 31.91 % 77 47 15 32 38.47
card/tmpsets/set/mod007-card-mod-machines/abstract/machine_input.rb 60.98 % 81 41 25 16 1.61
card/tmpsets/set/mod007-card-mod-machines/abstract/script.rb 56.82 % 93 44 25 19 0.75
card/tmpsets/set/mod007-card-mod-machines/abstract/skin_box.rb 66.67 % 42 21 14 7 1.71
card/tmpsets/set/mod007-card-mod-machines/all/reset_machines.rb 38.89 % 31 18 7 11 0.50
card/tmpsets/set/mod007-card-mod-machines/right/machine_cache.rb 85.71 % 14 7 6 1 1.14
card/tmpsets/set/mod007-card-mod-machines/right/machine_input.rb 75.00 % 16 8 6 2 1.00
card/tmpsets/set/mod007-card-mod-machines/right/machine_output.rb 44.44 % 53 27 12 15 0.59
card/tmpsets/set/mod007-card-mod-machines/self/script_decko.rb 75.00 % 21 8 6 2 1.00
card/tmpsets/set/mod007-card-mod-machines/self/script_html5shiv_printshiv.rb 100.00 % 20 8 8 0 29.25
card/tmpsets/set/mod007-card-mod-machines/self/script_jquery.rb 77.78 % 19 9 7 2 1.00
card/tmpsets/set/mod007-card-mod-machines/self/script_jquery_helper.rb 87.50 % 23 8 7 1 1.13
card/tmpsets/set/mod007-card-mod-machines/self/style_bootstrap_compatible.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod007-card-mod-machines/self/style_cards.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod007-card-mod-machines/self/style_jquery_ui_smoothness.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod007-card-mod-machines/type/coffee_script.rb 64.71 % 34 17 11 6 0.88
card/tmpsets/set/mod007-card-mod-machines/type/css.rb 59.52 % 94 42 25 17 0.71
card/tmpsets/set/mod007-card-mod-machines/type/java_script.rb 87.50 % 18 8 7 1 1.25
card/tmpsets/set/mod007-card-mod-machines/type/scss.rb 71.43 % 30 14 10 4 1.00
card/tmpsets/set/mod007-card-mod-machines/type/skin.rb 88.89 % 18 9 8 1 1.11
card/tmpsets/set/mod008-settings/abstract/permission.rb 37.93 % 131 58 22 36 5.64
card/tmpsets/set/mod008-settings/abstract/templated_nests.rb 100.00 % 16 8 8 0 4.38
card/tmpsets/set/mod008-settings/all/supports_content_options.rb 75.00 % 16 8 6 2 1.00
card/tmpsets/set/mod008-settings/right/autoname.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod008-settings/right/comment.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod008-settings/right/content_option_view.rb 66.67 % 19 9 6 3 1.00
card/tmpsets/set/mod008-settings/right/content_options.rb 61.54 % 27 13 8 5 0.85
card/tmpsets/set/mod008-settings/right/create.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod008-settings/right/default.rb 52.38 % 43 21 11 10 0.67
card/tmpsets/set/mod008-settings/right/delete.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod008-settings/right/guide.rb 85.71 % 17 7 6 1 1.29
card/tmpsets/set/mod008-settings/right/help.rb 72.73 % 24 11 8 3 1.00
card/tmpsets/set/mod008-settings/right/input_type.rb 65.00 % 44 20 13 7 1.05
card/tmpsets/set/mod008-settings/right/read.rb 40.91 % 88 44 18 26 0.48
card/tmpsets/set/mod008-settings/right/script.rb 84.62 % 28 13 11 2 1.08
card/tmpsets/set/mod008-settings/right/structure.rb 75.00 % 81 40 30 10 3.73
card/tmpsets/set/mod008-settings/right/style.rb 58.82 % 65 34 20 14 7.32
card/tmpsets/set/mod008-settings/right/update.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod008-settings/self/autoname.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/captcha.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/content_option_view.rb 85.71 % 19 7 6 1 1.14
card/tmpsets/set/mod008-settings/self/content_options.rb 85.71 % 19 7 6 1 1.14
card/tmpsets/set/mod008-settings/self/create.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/csv_structure.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/default.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/default_html_view.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/delete.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/follow_fields.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/guide.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/head.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/help.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/input_type.rb 88.89 % 22 9 8 1 1.22
card/tmpsets/set/mod008-settings/self/layout.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/on_create.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/on_delete.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/on_update.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/read.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/recent_settings.rb 87.50 % 16 8 7 1 14.88
card/tmpsets/set/mod008-settings/self/script.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/structure.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/style.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/table_of_contents.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/thanks.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/update.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/type/setting.rb 51.52 % 88 33 17 16 1.21
card/tmpsets/set/mod009-card-mod-search/abstract/00_filter_helper.rb 83.33 % 64 30 25 5 62.70
card/tmpsets/set/mod009-card-mod-search/abstract/02_search_params.rb 90.91 % 67 33 30 3 8.24
card/tmpsets/set/mod009-card-mod-search/abstract/03_filter.rb 75.00 % 32 16 12 4 1.06
card/tmpsets/set/mod009-card-mod-search/abstract/03_filter/filter_form.rb 44.44 % 107 45 20 25 0.53
card/tmpsets/set/mod009-card-mod-search/abstract/03_filter/form_helper.rb 35.94 % 114 64 23 41 0.42
card/tmpsets/set/mod009-card-mod-search/abstract/03_filter/query_construction.rb 68.18 % 45 22 15 7 34.36
card/tmpsets/set/mod009-card-mod-search/abstract/04_right_filter_form.rb 68.75 % 35 16 11 5 0.88
card/tmpsets/set/mod009-card-mod-search/abstract/05_search.rb 76.47 % 93 51 39 12 5.63
card/tmpsets/set/mod009-card-mod-search/abstract/05_search/views.rb 60.47 % 168 86 52 34 2.81
card/tmpsets/set/mod009-card-mod-search/abstract/06_cql_search.rb 93.65 % 117 63 59 4 80.92
card/tmpsets/set/mod009-card-mod-search/right/children.rb 83.33 % 13 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/created.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/edited.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/editors.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/follow.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/linked_to_by.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/links_to.rb 83.33 % 14 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/mates.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/nested_by.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/nests.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/referred_to_by.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/refers_to.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/self/recent.rb 66.67 % 44 24 16 8 19.46
card/tmpsets/set/mod009-card-mod-search/self/search.rb 86.54 % 105 52 45 7 2.44
card/tmpsets/set/mod009-card-mod-search/type/search_type.rb 54.35 % 89 46 25 21 2.70
card/tmpsets/set/mod010-standard/all/comment.rb 44.44 % 89 45 20 25 7.09
card/tmpsets/set/mod010-standard/all/error.rb 54.55 % 85 44 24 20 0.64
card/tmpsets/set/mod010-standard/all/links.rb 88.73 % 158 71 63 8 1129.61
card/tmpsets/set/mod010-standard/all/list_changes.rb 73.08 % 58 26 19 7 16.42
card/tmpsets/set/mod010-standard/all/path.rb 96.10 % 184 77 74 3 1787.64
card/tmpsets/set/mod010-standard/all/rich_html.rb 100.00 % 13 6 6 0 1.50
card/tmpsets/set/mod010-standard/all/rich_html/alert.rb 100.00 % 32 17 17 0 28.53
card/tmpsets/set/mod010-standard/all/rich_html/content.rb 63.92 % 177 97 62 35 32.51
card/tmpsets/set/mod010-standard/all/rich_html/error.rb 59.18 % 204 98 58 40 2.71
card/tmpsets/set/mod010-standard/all/rich_html/frame.rb 96.97 % 62 33 32 1 48.27
card/tmpsets/set/mod010-standard/all/rich_html/header.rb 77.78 % 70 36 28 8 75.64
card/tmpsets/set/mod010-standard/all/rich_html/html_views/guide.rb 81.82 % 60 33 27 6 32.30
card/tmpsets/set/mod010-standard/all/rich_html/html_views/help.rb 93.55 % 58 31 29 2 160.35
card/tmpsets/set/mod010-standard/all/rich_html/html_views/info.rb 38.10 % 93 42 16 26 6.38
card/tmpsets/set/mod010-standard/all/rich_html/html_views/size.rb 61.54 % 26 13 8 5 1.00
card/tmpsets/set/mod010-standard/all/rich_html/menu.rb 87.14 % 138 70 61 9 112.31
card/tmpsets/set/mod010-standard/all/rich_html/modal.rb 80.00 % 128 65 52 13 13.66
card/tmpsets/set/mod010-standard/all/rich_html/overlay.rb 37.10 % 113 62 23 39 0.44
card/tmpsets/set/mod010-standard/all/rich_html/process_layout.rb 93.02 % 80 43 40 3 108.79
card/tmpsets/set/mod010-standard/all/rich_html/show.rb 84.00 % 54 25 21 4 70.36
card/tmpsets/set/mod010-standard/all/rich_html/title.rb 89.29 % 56 28 25 3 490.86
card/tmpsets/set/mod010-standard/all/rich_html/wrapper.rb 86.59 % 156 82 71 11 1223.33
card/tmpsets/set/mod010-standard/right/discussion.rb 60.00 % 18 10 6 4 0.80
card/tmpsets/set/mod010-standard/right/head.rb 84.62 % 26 13 11 2 35.38
card/tmpsets/set/mod010-standard/right/type_plus_right.rb 83.33 % 13 6 5 1 1.17
card/tmpsets/set/mod010-standard/right/when_created.rb 71.43 % 15 7 5 2 1.00
card/tmpsets/set/mod010-standard/right/when_last_edited.rb 71.43 % 15 7 5 2 1.00
card/tmpsets/set/mod010-standard/self/alerts.rb 83.33 % 14 6 5 1 1.17
card/tmpsets/set/mod010-standard/self/cardtype.rb 52.63 % 51 19 10 9 3.05
card/tmpsets/set/mod010-standard/self/codenames.rb 100.00 % 10 4 4 0 1.50
card/tmpsets/set/mod010-standard/self/foot.rb 83.33 % 15 6 5 1 1.17
card/tmpsets/set/mod010-standard/self/home.rb 88.89 % 18 9 8 1 50.89
card/tmpsets/set/mod010-standard/self/now.rb 83.33 % 15 6 5 1 1.17
card/tmpsets/set/mod010-standard/self/sidebar.rb 83.33 % 14 6 5 1 1.17
card/tmpsets/set/mod010-standard/type/basic.rb 39.02 % 72 41 16 25 21.63
card/tmpsets/set/mod010-standard/type/cardtype.rb 71.62 % 136 74 53 21 2.64
card/tmpsets/set/mod010-standard/type/layout_type.rb 66.67 % 25 12 8 4 0.92
card/tmpsets/set/mod010-standard/type/notification_template.rb 60.87 % 42 23 14 9 0.74
card/tmpsets/set/mod010-standard/type/number.rb 53.33 % 31 15 8 7 0.73
card/tmpsets/set/mod010-standard/type/phrase.rb 100.00 % 14 7 7 0 15.43
card/tmpsets/set/mod010-standard/type/session.rb 69.44 % 70 36 25 11 70.92
card/tmpsets/set/mod010-standard/type/toggle.rb 75.00 % 47 24 18 6 1.00
card/tmpsets/set/mod010-standard/type/uri.rb 75.00 % 24 12 9 3 1.08
card/tmpsets/set/mod011-card-mod-email/abstract/email_field.rb 90.00 % 50 20 18 2 8.00
card/tmpsets/set/mod011-card-mod-email/abstract/test_context.rb 84.21 % 35 19 16 3 8.21
card/tmpsets/set/mod011-card-mod-email/all/email_html.rb 77.78 % 18 9 7 2 1.11
card/tmpsets/set/mod011-card-mod-email/all/email_text.rb 72.73 % 23 11 8 3 1.00
card/tmpsets/set/mod011-card-mod-email/right/bcc.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod011-card-mod-email/right/cc.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod011-card-mod-email/right/from.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod011-card-mod-email/right/html_message.rb 91.67 % 22 12 11 1 5.92
card/tmpsets/set/mod011-card-mod-email/right/subject.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod011-card-mod-email/right/text_message.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod011-card-mod-email/right/to.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod011-card-mod-email/type/email_template.rb 71.93 % 100 57 41 16 9.46
card/tmpsets/set/mod011-card-mod-email/type/email_template/email_config.rb 95.74 % 90 47 45 2 32.02
card/tmpsets/set/mod012-card-mod-account/abstract/account_field.rb 100.00 % 26 11 11 0 3.64
card/tmpsets/set/mod012-card-mod-account/abstract/accountable.rb 72.00 % 58 25 18 7 5.56
card/tmpsets/set/mod012-card-mod-account/all/account.rb 90.74 % 106 54 49 5 156.04
card/tmpsets/set/mod012-card-mod-account/right/account.rb 91.43 % 67 35 32 3 6.74
card/tmpsets/set/mod012-card-mod-account/right/account/events.rb 86.54 % 107 52 45 7 1.17
card/tmpsets/set/mod012-card-mod-account/right/account/views.rb 88.57 % 74 35 31 4 1.69
card/tmpsets/set/mod012-card-mod-account/right/api_key.rb 57.14 % 57 14 8 6 0.71
card/tmpsets/set/mod012-card-mod-account/right/email.rb 81.48 % 52 27 22 5 4.48
card/tmpsets/set/mod012-card-mod-account/right/password.rb 96.55 % 60 29 28 1 15.24
card/tmpsets/set/mod012-card-mod-account/right/roles.rb 94.74 % 36 19 18 1 1.16
card/tmpsets/set/mod012-card-mod-account/right/salt.rb 90.91 % 22 11 10 1 1.45
card/tmpsets/set/mod012-card-mod-account/right/status.rb 78.57 % 27 14 11 3 0.93
card/tmpsets/set/mod012-card-mod-account/self/signin.rb 80.67 % 235 119 96 23 23.62
card/tmpsets/set/mod012-card-mod-account/type/role.rb 60.00 % 30 15 9 6 0.80
card/tmpsets/set/mod012-card-mod-account/type/signup.rb 78.13 % 63 32 25 7 0.88
card/tmpsets/set/mod012-card-mod-account/type/signup/views.rb 54.55 % 102 55 30 25 0.65
card/tmpsets/set/mod012-card-mod-account/type/user.rb 100.00 % 77 36 36 0 1.22
card/tmpsets/set/mod012-card-mod-account/type_plus_right/user/email.rb 70.00 % 22 10 7 3 1.00
card/tmpsets/set/mod013-navbar/abstract/account_dropdown.rb 100.00 % 34 16 16 0 114.88
card/tmpsets/set/mod013-navbar/abstract/pointer/html_views.rb 100.00 % 14 7 7 0 33.43
card/tmpsets/set/mod013-navbar/all/navbar_links.rb 93.33 % 67 30 28 2 321.63
card/tmpsets/set/mod013-navbar/right/enabled_roles.rb 77.14 % 70 35 27 8 61.91
card/tmpsets/set/mod013-navbar/self/account_links.rb 85.71 % 108 49 42 7 118.24
card/tmpsets/set/mod013-navbar/self/dropdown_divider.rb 88.89 % 18 9 8 1 26.00
card/tmpsets/set/mod013-navbar/self/navbox.rb 100.00 % 45 18 18 0 87.89
card/tmpsets/set/mod013-navbar/type/html.rb 63.64 % 23 11 7 4 0.91
card/tmpsets/set/mod014-card-mod-edit/all/bridge.rb 44.12 % 73 34 15 19 0.53
card/tmpsets/set/mod014-card-mod-edit/all/bridge/bridge_pills.rb 38.71 % 56 31 12 19 0.52
card/tmpsets/set/mod014-card-mod-edit/all/bridge/follow_section.rb 47.62 % 46 21 10 11 0.67
card/tmpsets/set/mod014-card-mod-edit/all/bridge/related_section.rb 56.25 % 43 16 9 7 0.81
card/tmpsets/set/mod014-card-mod-edit/all/bridge/tab_views.rb 46.15 % 50 26 12 14 0.62
card/tmpsets/set/mod014-card-mod-edit/all/bridge/tab_visibility.rb 50.00 % 62 32 16 16 0.63
card/tmpsets/set/mod014-card-mod-edit/all/edit_content.rb 100.00 % 69 28 28 0 13.86
card/tmpsets/set/mod014-card-mod-edit/all/edit_inline.rb 75.00 % 65 28 21 7 31.61
card/tmpsets/set/mod014-card-mod-edit/all/edit_name.rb 45.16 % 67 31 14 17 0.55
card/tmpsets/set/mod014-card-mod-edit/all/edit_type.rb 77.78 % 110 54 42 12 10.89
card/tmpsets/set/mod014-card-mod-edit/all/editing.rb 56.25 % 100 32 18 14 2.53
card/tmpsets/set/mod014-card-mod-edit/all/editor.rb 90.00 % 65 30 27 3 44.60
card/tmpsets/set/mod014-card-mod-edit/all/form.rb 94.21 % 254 121 114 7 202.56
card/tmpsets/set/mod014-card-mod-edit/all/form_buttons.rb 88.10 % 73 42 37 5 12.24
card/tmpsets/set/mod014-card-mod-edit/all/form_elements.rb 92.86 % 80 42 39 3 162.95
card/tmpsets/set/mod014-card-mod-edit/all/formgroup.rb 100.00 % 46 27 27 0 164.78
card/tmpsets/set/mod014-card-mod-edit/all/new.rb 79.79 % 197 94 75 19 18.29
card/tmpsets/set/mod014-card-mod-edit/all/overlay_guide.rb 100.00 % 18 6 6 0 1.67
card/tmpsets/set/mod014-card-mod-edit/all/template_nest.rb 88.89 % 51 27 24 3 12.11
card/tmpsets/set/mod014-card-mod-edit/type/list.rb 66.67 % 24 12 8 4 0.83
card/tmpsets/set/mod014-card-mod-edit/type/plain_text.rb 72.73 % 22 11 8 3 1.00
card/tmpsets/set/mod014-card-mod-edit/type/pointer.rb 70.00 % 20 10 7 3 0.90
card/tmpsets/set/mod015-card-mod-ace_editor/all/ace_editor.rb 100.00 % 21 9 9 0 1.78
card/tmpsets/set/mod015-card-mod-ace_editor/self/ace.rb 85.71 % 16 7 6 1 1.29
card/tmpsets/set/mod015-card-mod-ace_editor/self/script_ace.rb 100.00 % 13 7 7 0 1.29
card/tmpsets/set/mod015-card-mod-ace_editor/self/script_ace_config.rb 100.00 % 13 7 7 0 1.29
card/tmpsets/set/mod016-card-mod-bar_and_box/abstract/media.rb 100.00 % 26 14 14 0 3.36
card/tmpsets/set/mod016-card-mod-bar_and_box/all/bar.rb 78.33 % 114 60 47 13 19.40
card/tmpsets/set/mod016-card-mod-bar_and_box/all/box.rb 66.67 % 25 12 8 4 0.92
card/tmpsets/set/mod016-card-mod-bar_and_box/self/style_media.rb 87.50 % 15 8 7 1 1.13
card/tmpsets/set/mod016-card-mod-bar_and_box/type/image.rb 90.91 % 47 22 20 2 3.27
card/tmpsets/set/mod017-card-mod-bootstrap/abstract/bootstrap_code_file.rb 54.29 % 66 35 19 16 0.71
card/tmpsets/set/mod017-card-mod-bootstrap/abstract/bootswatch_theme.rb 53.57 % 166 56 30 26 0.57
card/tmpsets/set/mod017-card-mod-bootstrap/abstract/bootswatch_theme/html_views.rb 80.00 % 39 20 16 4 2.20
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/accordion.rb 28.57 % 72 28 8 20 0.43
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/dropdown.rb 82.35 % 95 34 28 6 185.65
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/form.rb 100.00 % 44 23 23 0 115.91
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/helper.rb 40.00 % 62 40 16 24 25.90
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/icon.rb 91.18 % 118 34 31 3 267.29
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/navbar.rb 33.33 % 75 33 11 22 0.45
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/table.rb 29.09 % 108 55 16 39 0.36
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/tabs.rb 100.00 % 49 9 9 0 1.78
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/wrapper.rb 93.33 % 28 15 14 1 21.60
card/tmpsets/set/mod017-card-mod-bootstrap/all/rich_bootstrap.rb 66.67 % 24 12 8 4 0.92
card/tmpsets/set/mod017-card-mod-bootstrap/self/bootstrap_core.rb 54.55 % 22 11 6 5 0.73
card/tmpsets/set/mod017-card-mod-bootstrap/self/bootstrap_functions.rb 85.71 % 14 7 6 1 1.14
card/tmpsets/set/mod017-card-mod-bootstrap/self/font_awesome.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod017-card-mod-bootstrap/self/material_icons.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod017-card-mod-bootstrap/self/script_bootstrap.rb 80.00 % 21 10 8 2 1.00
card/tmpsets/set/mod017-card-mod-bootstrap/self/script_load_select2.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod017-card-mod-bootstrap/self/script_select2.rb 80.00 % 21 10 8 2 1.00
card/tmpsets/set/mod017-card-mod-bootstrap/self/smartmenu_css.rb 85.71 % 14 7 6 1 1.14
card/tmpsets/set/mod017-card-mod-bootstrap/self/smartmenu_js.rb 85.71 % 14 7 6 1 1.14
card/tmpsets/set/mod017-card-mod-bootstrap/self/style_bootstrap_cards.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod017-card-mod-bootstrap/self/style_bootstrap_colorpicker.rb 87.50 % 15 8 7 1 1.13
card/tmpsets/set/mod017-card-mod-bootstrap/self/style_select2.rb 80.00 % 19 10 8 2 1.00
card/tmpsets/set/mod017-card-mod-bootstrap/self/style_select2_bootstrap.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod017-card-mod-bootstrap/type/bootswatch_skin.rb 77.78 % 24 9 7 2 1.00
card/tmpsets/set/mod017-card-mod-bootstrap/type/customized_bootswatch_skin.rb 41.18 % 124 68 28 40 0.46
card/tmpsets/set/mod017-card-mod-bootstrap/type/customized_bootswatch_skin/html_views.rb 65.22 % 45 23 15 8 0.87
card/tmpsets/set/mod017-card-mod-bootstrap/type_plus_right/customized_bootswatch_skin/colors.rb 44.23 % 122 52 23 29 0.56
card/tmpsets/set/mod018-card-mod-history/all/history.rb 78.57 % 118 56 44 12 88.73
card/tmpsets/set/mod018-card-mod-history/all/history/act_listing.rb 55.88 % 133 68 38 30 0.81
card/tmpsets/set/mod018-card-mod-history/all/history/actions.rb 74.63 % 133 67 50 17 38.45
card/tmpsets/set/mod018-card-mod-history/all/history/acts.rb 75.00 % 17 8 6 2 1.13
card/tmpsets/set/mod018-card-mod-history/all/history/events.rb 88.00 % 109 50 44 6 85.64
card/tmpsets/set/mod018-card-mod-history/all/history/last.rb 65.45 % 107 55 36 19 9.78
card/tmpsets/set/mod018-card-mod-history/all/history/revision.rb 40.00 % 74 40 16 24 12.83
card/tmpsets/set/mod018-card-mod-history/all/history/selected.rb 100.00 % 73 39 39 0 339.72
card/tmpsets/set/mod018-card-mod-history/all/history/views.rb 59.09 % 44 22 13 9 0.86
card/tmpsets/set/mod018-card-mod-history/all/history_bridge.rb 31.71 % 74 41 13 28 0.39
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment.rb 78.13 % 121 64 50 14 46.44
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/cloud.rb 36.99 % 142 73 27 46 8.07
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/coded.rb 56.25 % 31 16 9 7 5.88
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/local.rb 82.14 % 49 28 23 5 3.43
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/paths.rb 91.43 % 67 35 32 3 620.69
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/storage_type.rb 70.21 % 179 94 66 28 544.70
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/upload_cache.rb 100.00 % 94 49 49 0 9.12
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/web.rb 100.00 % 12 6 6 0 3.00
card/tmpsets/set/mod019-card-mod-carrierwave/all/file_utils.rb 90.91 % 50 22 20 2 1.55
card/tmpsets/set/mod019-card-mod-carrierwave/self/admin.rb 60.00 % 32 10 6 4 0.80
card/tmpsets/set/mod019-card-mod-carrierwave/self/favicon.rb 92.31 % 25 13 12 1 69.77
card/tmpsets/set/mod019-card-mod-carrierwave/self/new_file.rb 72.73 % 22 11 8 3 1.00
card/tmpsets/set/mod019-card-mod-carrierwave/self/new_image.rb 90.91 % 22 11 10 1 1.73
card/tmpsets/set/mod019-card-mod-carrierwave/type/file.rb 90.91 % 125 66 60 6 41.17
card/tmpsets/set/mod019-card-mod-carrierwave/type/image.rb 78.33 % 110 60 47 13 96.23
card/tmpsets/set/mod019-card-mod-carrierwave/type/image/html_views.rb 64.10 % 78 39 25 14 12.90
card/tmpsets/set/mod020-card-mod-date/all/calendar.rb 87.50 % 18 8 7 1 1.25
card/tmpsets/set/mod020-card-mod-date/self/datepicker.rb 83.33 % 15 6 5 1 1.17
card/tmpsets/set/mod020-card-mod-date/self/script_datepicker.rb 87.50 % 16 8 7 1 1.13
card/tmpsets/set/mod020-card-mod-date/self/script_datepicker_config.rb 100.00 % 13 7 7 0 1.29
card/tmpsets/set/mod020-card-mod-date/self/style_datepicker.rb 80.00 % 20 10 8 2 1.00
card/tmpsets/set/mod020-card-mod-date/type/date.rb 85.71 % 14 7 6 1 1.29
card/tmpsets/set/mod022-card-mod-follow/abstract/follow_option.rb 93.10 % 65 29 27 2 3.14
card/tmpsets/set/mod022-card-mod-follow/all/follow.rb 70.00 % 42 20 14 6 5.35
card/tmpsets/set/mod022-card-mod-follow/all/follow/follow_link.rb 50.00 % 69 30 15 15 0.57
card/tmpsets/set/mod022-card-mod-follow/all/follow/follow_link_views.rb 68.42 % 38 19 13 6 1.00
card/tmpsets/set/mod022-card-mod-follow/all/follow/followed_by.rb 73.68 % 79 38 28 10 185.47
card/tmpsets/set/mod022-card-mod-follow/all/follow/follower_ids.rb 92.75 % 130 69 64 5 64.90
card/tmpsets/set/mod022-card-mod-follow/all/follow/start_follow_link.rb 50.00 % 19 10 5 5 0.70
card/tmpsets/set/mod022-card-mod-follow/all/follow/stop_follow_link.rb 45.45 % 20 11 5 6 0.64
card/tmpsets/set/mod022-card-mod-follow/all/notify.rb 95.45 % 90 44 42 2 60.89
card/tmpsets/set/mod022-card-mod-follow/all/notify/base_views.rb 94.37 % 136 71 67 4 29.39
card/tmpsets/set/mod022-card-mod-follow/all/notify/html_views.rb 100.00 % 26 11 11 0 6.64
card/tmpsets/set/mod022-card-mod-follow/right/account.rb 100.00 % 24 11 11 0 8.45
card/tmpsets/set/mod022-card-mod-follow/right/follow.rb 55.00 % 127 60 33 27 2.50
card/tmpsets/set/mod022-card-mod-follow/right/follow_fields.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod022-card-mod-follow/right/followers.rb 92.86 % 32 14 13 1 3.50
card/tmpsets/set/mod022-card-mod-follow/right/following.rb 43.33 % 59 30 13 17 0.53
card/tmpsets/set/mod022-card-mod-follow/self/always.rb 81.82 % 22 11 9 2 3.55
card/tmpsets/set/mod022-card-mod-follow/self/created.rb 78.57 % 28 14 11 3 24.79
card/tmpsets/set/mod022-card-mod-follow/self/edited.rb 78.57 % 29 14 11 3 24.79
card/tmpsets/set/mod022-card-mod-follow/self/follow.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod022-card-mod-follow/self/follow_defaults.rb 35.90 % 97 39 14 25 0.44
card/tmpsets/set/mod022-card-mod-follow/self/never.rb 81.82 % 22 11 9 2 1.18
card/tmpsets/set/mod022-card-mod-follow/type/cardtype.rb 66.67 % 30 15 10 5 0.87
card/tmpsets/set/mod022-card-mod-follow/type/set.rb 64.52 % 62 31 20 11 4.74
card/tmpsets/set/mod022-card-mod-follow/type/user.rb 55.56 % 16 9 5 4 0.78
card/tmpsets/set/mod022-card-mod-follow/type_plus_right/user/follow.rb 52.38 % 86 42 22 20 0.79
card/tmpsets/set/mod022-card-mod-follow/type_plus_right/user/follow/follow_editor_helper.rb 31.67 % 130 60 19 41 0.40
card/tmpsets/set/mod023-card-mod-google_analytics/all/google_analytics.rb 83.33 % 51 24 20 4 55.04
card/tmpsets/set/mod024-card-mod-markdown/type/markdown.rb 84.62 % 26 13 11 2 8.62
card/tmpsets/set/mod025-card-mod-prosemirror_editor/all/prosemirror_editor.rb 75.00 % 17 8 6 2 1.13
card/tmpsets/set/mod025-card-mod-prosemirror_editor/self/script_prosemirror.rb 100.00 % 13 7 7 0 1.29
card/tmpsets/set/mod025-card-mod-prosemirror_editor/self/script_prosemirror_config.rb 100.00 % 14 7 7 0 1.29
card/tmpsets/set/mod025-card-mod-prosemirror_editor/self/style_prosemirror.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod026-card-mod-recaptcha/all/recaptcha.rb 56.00 % 111 50 28 22 55.16
card/tmpsets/set/mod026-card-mod-recaptcha/self/admin_info.rb 56.25 % 41 16 9 7 0.75
card/tmpsets/set/mod026-card-mod-recaptcha/self/recaptcha_proxy.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod026-card-mod-recaptcha/self/recaptcha_secret_key.rb 66.67 % 18 9 6 3 0.89
card/tmpsets/set/mod026-card-mod-recaptcha/self/recaptcha_settings.rb 85.71 % 53 7 6 1 1.29
card/tmpsets/set/mod026-card-mod-recaptcha/self/recaptcha_site_key.rb 66.67 % 18 9 6 3 0.89
card/tmpsets/set/mod027-card-mod-rules/right/self.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod027-card-mod-rules/rstar/rule_user.rb 87.50 % 17 8 7 1 4.13
card/tmpsets/set/mod027-card-mod-rules/rule/bar_view.rb 69.39 % 101 49 34 15 0.82
card/tmpsets/set/mod027-card-mod-rules/rule/bridge_rules_editor.rb 72.22 % 37 18 13 5 3.89
card/tmpsets/set/mod027-card-mod-rules/rule/editor.rb 48.15 % 119 54 26 28 0.59
card/tmpsets/set/mod027-card-mod-rules/rule/html_views.rb 88.89 % 22 9 8 1 16.33
card/tmpsets/set/mod027-card-mod-rules/rule/quick_editor.rb 57.14 % 48 21 12 9 0.71
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form.rb 37.14 % 66 35 13 22 0.46
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/buttons.rb 56.52 % 55 23 13 10 0.87
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/form_elements.rb 33.33 % 61 33 11 22 0.45
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/rule_set_radio.rb 36.17 % 91 47 17 30 0.40
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/set_selection.rb 39.13 % 48 23 9 14 0.57
card/tmpsets/set/mod027-card-mod-rules/rule/rules.rb 52.54 % 118 59 31 28 3.69
card/tmpsets/set/mod027-card-mod-rules/self/script_rules.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod027-card-mod-rules/type/set.rb 56.45 % 116 62 35 27 51.56
card/tmpsets/set/mod027-card-mod-rules/type/set/html_views.rb 58.62 % 60 29 17 12 0.93
card/tmpsets/set/mod027-card-mod-rules/type/set/html_views/rule_lists.rb 50.00 % 51 26 13 13 0.69
card/tmpsets/set/mod027-card-mod-rules/type/set/html_views/template.rb 100.00 % 21 11 11 0 8.73
card/tmpsets/set/mod027-card-mod-rules/type/set/rules_filter.rb 46.43 % 69 28 13 15 0.61
card/tmpsets/set/mod027-card-mod-rules/type/set/setting_lists.rb 48.78 % 85 41 20 21 0.59
card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor.rb 52.38 % 41 21 11 10 0.67
card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor/link_editor.rb 57.89 % 42 19 11 8 0.79
card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor/link_editor/link_parser.rb 52.38 % 43 21 11 10 0.67
card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor/nest_editor.rb 33.33 % 185 87 29 58 0.38
card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor/nest_image.rb 41.67 % 79 36 15 21 0.56
card/tmpsets/set/mod028-card-mod-tinymce_editor/all/tinymce_editor.rb 100.00 % 16 7 7 0 9.29
card/tmpsets/set/mod028-card-mod-tinymce_editor/self/script_tinymce.rb 100.00 % 13 7 7 0 1.29
card/tmpsets/set/mod028-card-mod-tinymce_editor/self/script_tinymce_config.rb 100.00 % 13 7 7 0 1.29
card/tmpsets/set/mod028-card-mod-tinymce_editor/self/tiny_mce.rb 83.33 % 16 6 5 1 1.17
card/tmpsets/set/mod030-card-mod-monkey/all/event_viz.rb 33.33 % 73 36 12 24 0.39
card/tmpsets/set/mod030-card-mod-monkey/all/view_viz.rb 38.89 % 32 18 7 11 0.56
card/tmpsets/set/mod030-card-mod-monkey/right/debug.rb 30.61 % 97 49 15 34 0.37
card/tmpsets/set_pattern/100-all.rb 81.82 % 23 11 9 2 13.45
card/tmpsets/set_pattern/101-all_plus.rb 75.00 % 25 12 9 3 0.75
card/tmpsets/set_pattern/102-type.rb 95.00 % 41 20 19 1 1029.00
card/tmpsets/set_pattern/103-star.rb 76.92 % 27 13 10 3 591.00
card/tmpsets/set_pattern/104-rstar.rb 85.71 % 29 14 12 2 548.93
card/tmpsets/set_pattern/105-rule.rb 85.71 % 29 14 12 2 549.64
card/tmpsets/set_pattern/106-right.rb 81.25 % 36 16 13 3 278.19
card/tmpsets/set_pattern/107-type_plus_right.rb 77.78 % 44 18 14 4 244.39
card/tmpsets/set_pattern/108-self.rb 94.12 % 35 17 16 1 911.00
cardname/lib/cardname.rb 90.24 % 152 82 74 8 53327.32
cardname/lib/cardname/contextual.rb 82.43 % 128 74 61 13 3503.82
cardname/lib/cardname/manipulate.rb 32.61 % 78 46 15 31 40.85
cardname/lib/cardname/parts.rb 72.41 % 139 58 42 16 5964.95
cardname/lib/cardname/predicates.rb 76.47 % 30 17 13 4 226.53
cardname/lib/cardname/variants.rb 100.00 % 36 16 16 0 1658.25
decko-cucumber/lib/decko/cucumber.rb 100.00 % 9 9 9 0 1.00
decko-spring/lib/decko/spring.rb 100.00 % 4 3 3 0 1.00
decko/lib/decko.rb 92.31 % 26 13 12 1 2.69
decko/lib/decko/application.rb 97.62 % 90 42 41 1 30.88
decko/lib/decko/engine.rb 100.00 % 57 29 29 0 1.07
decko/lib/decko/response.rb 87.50 % 170 88 77 11 69.34
decko/rails/controllers/application_controller.rb 100.00 % 2 1 1 0 1.00
decko/rails/controllers/card_controller.rb 96.15 % 151 78 75 3 114.24
decko/rails/engine-routes.rb 100.00 % 53 27 27 0 1.22

Card ( 85.01% covered at 3793.59 hits/line )

136 files in total.
5222 relevant lines, 4439 lines covered and 783 lines missed. ( 85.01% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/lib/card.rb 100.00 % 157 20 20 0 33.25
card/lib/card/auth.rb 88.89 % 45 18 16 2 20.50
card/lib/card/auth/current.rb 90.41 % 158 73 66 7 455.34
card/lib/card/auth/permissions.rb 96.88 % 72 32 31 1 1237.16
card/lib/card/auth/proxy.rb 96.30 % 59 27 26 1 1923.44
card/lib/card/auth/setup.rb 89.29 % 55 28 25 3 7.68
card/lib/card/auth/token.rb 94.74 % 38 19 18 1 1.89
card/lib/card/cache.rb 77.33 % 181 75 58 17 26580.75
card/lib/card/cache/persistent.rb 89.06 % 154 64 57 7 3383.67
card/lib/card/cache/prepopulate.rb 45.00 % 48 20 9 11 0.45
card/lib/card/cache/temporary.rb 86.36 % 58 22 19 3 23976.95
card/lib/card/codename.rb 88.33 % 153 60 53 7 5431.45
card/lib/card/content.rb 86.11 % 153 72 62 10 873.57
card/lib/card/content/chunk.rb 95.65 % 96 46 44 2 724.80
card/lib/card/content/chunk/abstract.rb 92.86 % 88 42 39 3 2226.74
card/lib/card/content/clean.rb 76.00 % 95 50 38 12 45.92
card/lib/card/content/diff.rb 78.79 % 70 33 26 7 1.67
card/lib/card/content/diff/l_c_s.rb 75.51 % 90 49 37 12 1.80
card/lib/card/content/diff/l_c_s/processor.rb 80.65 % 163 93 75 18 3.17
card/lib/card/content/diff/result.rb 69.15 % 159 94 65 29 2.05
card/lib/card/content/parser.rb 93.88 % 104 49 46 3 3416.55
card/lib/card/content/truncate.rb 26.32 % 67 38 10 28 0.26
card/lib/card/director.rb 90.48 % 231 63 57 6 286.81
card/lib/card/director/act_direction.rb 91.80 % 111 61 56 5 264.72
card/lib/card/director/card_methods.rb 100.00 % 45 23 23 0 610.22
card/lib/card/director/event_delay.rb 100.00 % 44 19 19 0 31.79
card/lib/card/director/phases.rb 100.00 % 55 32 32 0 161.13
card/lib/card/director/run.rb 93.85 % 118 65 61 4 1365.48
card/lib/card/director/stages.rb 79.55 % 85 44 35 9 2652.36
card/lib/card/director/store.rb 100.00 % 72 33 33 0 122.45
card/lib/card/director/subdirector_array.rb 100.00 % 51 29 29 0 203.59
card/lib/card/dirty.rb 100.00 % 70 34 34 0 1423.15
card/lib/card/dirty/method_factory.rb 100.00 % 15 8 8 0 1630.25
card/lib/card/env.rb 83.93 % 97 56 47 9 1821.61
card/lib/card/env/location.rb 72.73 % 44 22 16 6 1125.45
card/lib/card/env/location_history.rb 96.43 % 54 28 27 1 105.04
card/lib/card/env/request_assignments.rb 100.00 % 24 12 12 0 148.67
card/lib/card/env/serialization.rb 100.00 % 24 12 12 0 147.25
card/lib/card/env/slot_options.rb 100.00 % 30 17 17 0 187.24
card/lib/card/env/success.rb 84.78 % 171 92 78 14 85.22
card/lib/card/error.rb 81.18 % 173 85 69 16 3.38
card/lib/card/format.rb 85.71 % 94 49 42 7 2059.02
card/lib/card/format/content.rb 92.86 % 89 42 39 3 2266.62
card/lib/card/format/context_names.rb 89.66 % 57 29 26 3 935.14
card/lib/card/format/error.rb 52.17 % 80 46 24 22 1306.04
card/lib/card/format/method_delegation.rb 100.00 % 71 33 33 0 29670.48
card/lib/card/format/nest.rb 100.00 % 81 39 39 0 3463.62
card/lib/card/format/nest/fetch.rb 85.37 % 75 41 35 6 1874.71
card/lib/card/format/nesting.rb 75.68 % 74 37 28 9 1498.30
card/lib/card/format/nesting/main.rb 66.67 % 48 24 16 8 191.13
card/lib/card/format/nesting/mode.rb 81.25 % 88 32 26 6 1455.13
card/lib/card/format/nesting/subformat.rb 81.82 % 64 33 27 6 2601.70
card/lib/card/format/registration.rb 100.00 % 53 31 31 0 2324.29
card/lib/card/format/render.rb 67.53 % 148 77 52 25 9097.56
card/lib/card/format/wrapper.rb 87.50 % 49 24 21 3 3856.50
card/lib/card/lexicon.rb 95.24 % 90 42 40 2 13896.19
card/lib/card/mailer.rb 100.00 % 38 18 18 0 8.72
card/lib/card/mark.rb 72.41 % 67 29 21 8 66023.34
card/lib/card/mod.rb 94.74 % 95 19 18 1 119.42
card/lib/card/mod/dirs.rb 90.91 % 181 77 70 7 815.38
card/lib/card/mod/load_strategy.rb 76.92 % 72 39 30 9 35.08
card/lib/card/mod/load_strategy/pattern_tmp_files.rb 100.00 % 29 17 17 0 3.41
card/lib/card/mod/load_strategy/set_tmp_files.rb 100.00 % 34 19 19 0 124.74
card/lib/card/mod/load_strategy/tmp_files.rb 100.00 % 43 23 23 0 115.17
card/lib/card/mod/loader.rb 80.95 % 94 42 34 8 24.45
card/lib/card/mod/loader/set_loader.rb 91.25 % 159 80 73 7 190.36
card/lib/card/mod/loader/set_pattern_loader.rb 80.00 % 59 25 20 5 2.08
card/lib/card/mod/module_template.rb 97.30 % 83 37 36 1 267.08
card/lib/card/name.rb 93.18 % 87 44 41 3 86645.16
card/lib/card/name/fields_and_traits.rb 96.55 % 64 29 28 1 683.45
card/lib/card/name/name_variants.rb 58.82 % 36 17 10 7 0.59
card/lib/card/query.rb 100.00 % 110 23 23 0 221.26
card/lib/card/query/abstract_query.rb 91.67 % 95 48 44 4 1168.29
card/lib/card/query/abstract_query/query_helper.rb 100.00 % 56 30 30 0 1127.77
card/lib/card/query/abstract_query/tie.rb 88.46 % 104 52 46 6 629.62
card/lib/card/query/act_query.rb 84.62 % 26 13 11 2 102.46
card/lib/card/query/action_query.rb 68.42 % 36 19 13 6 70.21
card/lib/card/query/card_query.rb 96.67 % 59 30 29 1 308.67
card/lib/card/query/card_query/conjunctions.rb 92.31 % 48 26 24 2 43.08
card/lib/card/query/card_query/found_by.rb 90.48 % 43 21 19 2 3.19
card/lib/card/query/card_query/interpretation.rb 75.51 % 100 49 37 12 503.69
card/lib/card/query/card_query/match_attributes.rb 85.00 % 44 20 17 3 2.55
card/lib/card/query/card_query/normalization.rb 87.88 % 60 33 29 4 621.12
card/lib/card/query/card_query/reference_attributes.rb 86.67 % 45 15 13 2 16.60
card/lib/card/query/card_query/relational_attributes.rb 77.78 % 95 45 35 10 26.02
card/lib/card/query/card_query/run.rb 81.67 % 112 60 49 11 244.33
card/lib/card/query/card_query/sorting.rb 32.65 % 101 49 16 33 0.33
card/lib/card/query/clause.rb 100.00 % 19 9 9 0 292.89
card/lib/card/query/join.rb 82.00 % 126 50 41 9 1149.86
card/lib/card/query/reference_query.rb 90.00 % 54 30 27 3 112.40
card/lib/card/query/sql_statement.rb 92.42 % 120 66 61 5 379.32
card/lib/card/query/sql_statement/joins.rb 97.30 % 62 37 36 1 854.11
card/lib/card/query/sql_statement/order.rb 90.00 % 68 30 27 3 346.40
card/lib/card/query/sql_statement/where.rb 87.04 % 102 54 47 7 711.54
card/lib/card/query/value.rb 88.68 % 94 53 47 6 284.64
card/lib/card/query/value/match_value.rb 96.77 % 65 31 30 1 5.68
card/lib/card/reference.rb 52.94 % 85 34 18 16 51.97
card/lib/card/set.rb 100.00 % 85 24 24 0 1.00
card/lib/card/set/abstract.rb 100.00 % 6 3 3 0 1.00
card/lib/card/set/advanced_api.rb 78.38 % 71 37 29 8 10.24
card/lib/card/set/basket.rb 89.47 % 59 19 17 2 65.74
card/lib/card/set/event.rb 98.25 % 177 57 56 1 912.77
card/lib/card/set/event/callbacks.rb 93.75 % 30 16 15 1 3004.69
card/lib/card/set/event/delayed_event.rb 76.12 % 145 67 51 16 78.28
card/lib/card/set/event/options.rb 92.86 % 80 42 39 3 119.52
card/lib/card/set/format.rb 97.78 % 152 45 44 1 2523.09
card/lib/card/set/format/abstract_format.rb 92.00 % 126 25 23 2 46.40
card/lib/card/set/format/abstract_format/haml_views.rb 80.00 % 44 15 12 3 3.00
card/lib/card/set/format/abstract_format/view_definition.rb 80.00 % 67 35 28 7 126.03
card/lib/card/set/format/abstract_format/view_opts.rb 97.06 % 68 34 33 1 788.71
card/lib/card/set/format/abstract_format/wrapper.rb 96.77 % 91 31 30 1 96.55
card/lib/card/set/format/haml_paths.rb 82.00 % 85 50 41 9 737.78
card/lib/card/set/helpers.rb 95.65 % 44 23 22 1 2528.70
card/lib/card/set/i18n_scope.rb 90.00 % 111 40 36 4 42.38
card/lib/card/set/inheritance.rb 93.10 % 87 29 27 2 167.97
card/lib/card/set/loader.rb 100.00 % 72 26 26 0 138.96
card/lib/card/set/pattern.rb 48.15 % 47 27 13 14 308.15
card/lib/card/set/pattern/base.rb 95.24 % 157 84 80 4 18678.56
card/lib/card/set/required_field.rb 85.00 % 113 60 51 9 1.30
card/lib/card/set/trait.rb 88.89 % 77 45 40 5 41.36
card/lib/card/set/type.rb 100.00 % 46 26 26 0 2950.08
card/lib/card/subcards.rb 78.57 % 114 56 44 12 257.75
card/lib/card/subcards/add.rb 80.00 % 120 60 48 12 134.20
card/lib/card/subcards/relate.rb 78.57 % 27 14 11 3 52.93
card/lib/card/subcards/remove.rb 66.67 % 44 27 18 9 8.33
card/lib/card/version.rb 100.00 % 11 5 5 0 45.60
card/lib/card/view.rb 84.09 % 119 44 37 7 13257.84
card/lib/card/view/cache.rb 48.21 % 298 56 27 29 780.73
card/lib/card/view/cache/cache_action.rb 61.36 % 121 44 27 17 3972.80
card/lib/card/view/cache/stub.rb 60.00 % 46 15 9 6 0.60
card/lib/card/view/classy.rb 76.81 % 168 69 53 16 9807.33
card/lib/card/view/options.rb 66.67 % 93 15 10 5 0.67
card/lib/card/view/options/key_lists.rb 83.33 % 45 18 15 3 5307.78
card/lib/card/view/options/visibility.rb 93.18 % 99 44 41 3 12851.25
card/lib/card/view/options/voo_api.rb 92.22 % 204 90 83 7 22599.60
card/lib/card/view/permission.rb 100.00 % 54 25 25 0 8687.32

Decko ( 92.44% covered at 43.4 hits/line )

4 files in total.
172 relevant lines, 159 lines covered and 13 lines missed. ( 92.44% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
decko/lib/decko.rb 92.31 % 26 13 12 1 2.69
decko/lib/decko/application.rb 97.62 % 90 42 41 1 30.88
decko/lib/decko/engine.rb 100.00 % 57 29 29 0 1.07
decko/lib/decko/response.rb 87.50 % 170 88 77 11 69.34

Mod: settings ( 73.56% covered at 3.31 hits/line )

46 files in total.
556 relevant lines, 409 lines covered and 147 lines missed. ( 73.56% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/mod/settings/lib/card/setting.rb 85.42 % 99 48 41 7 11.77
card/tmpsets/set/mod008-settings/abstract/permission.rb 37.93 % 131 58 22 36 5.64
card/tmpsets/set/mod008-settings/abstract/templated_nests.rb 100.00 % 16 8 8 0 4.38
card/tmpsets/set/mod008-settings/all/supports_content_options.rb 75.00 % 16 8 6 2 1.00
card/tmpsets/set/mod008-settings/right/autoname.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod008-settings/right/comment.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod008-settings/right/content_option_view.rb 66.67 % 19 9 6 3 1.00
card/tmpsets/set/mod008-settings/right/content_options.rb 61.54 % 27 13 8 5 0.85
card/tmpsets/set/mod008-settings/right/create.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod008-settings/right/default.rb 52.38 % 43 21 11 10 0.67
card/tmpsets/set/mod008-settings/right/delete.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod008-settings/right/guide.rb 85.71 % 17 7 6 1 1.29
card/tmpsets/set/mod008-settings/right/help.rb 72.73 % 24 11 8 3 1.00
card/tmpsets/set/mod008-settings/right/input_type.rb 65.00 % 44 20 13 7 1.05
card/tmpsets/set/mod008-settings/right/read.rb 40.91 % 88 44 18 26 0.48
card/tmpsets/set/mod008-settings/right/script.rb 84.62 % 28 13 11 2 1.08
card/tmpsets/set/mod008-settings/right/structure.rb 75.00 % 81 40 30 10 3.73
card/tmpsets/set/mod008-settings/right/style.rb 58.82 % 65 34 20 14 7.32
card/tmpsets/set/mod008-settings/right/update.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod008-settings/self/autoname.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/captcha.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/content_option_view.rb 85.71 % 19 7 6 1 1.14
card/tmpsets/set/mod008-settings/self/content_options.rb 85.71 % 19 7 6 1 1.14
card/tmpsets/set/mod008-settings/self/create.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/csv_structure.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/default.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/default_html_view.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/delete.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/follow_fields.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/guide.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/head.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/help.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/input_type.rb 88.89 % 22 9 8 1 1.22
card/tmpsets/set/mod008-settings/self/layout.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/on_create.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/on_delete.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/on_update.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/read.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/recent_settings.rb 87.50 % 16 8 7 1 14.88
card/tmpsets/set/mod008-settings/self/script.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/structure.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/style.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/table_of_contents.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/thanks.rb 100.00 % 14 6 6 0 1.33
card/tmpsets/set/mod008-settings/self/update.rb 100.00 % 13 6 6 0 1.33
card/tmpsets/set/mod008-settings/type/setting.rb 51.52 % 88 33 17 16 1.21

Mod: navbar ( 87.43% covered at 122.9 hits/line )

8 files in total.
175 relevant lines, 153 lines covered and 22 lines missed. ( 87.43% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod013-navbar/abstract/account_dropdown.rb 100.00 % 34 16 16 0 114.88
card/tmpsets/set/mod013-navbar/abstract/pointer/html_views.rb 100.00 % 14 7 7 0 33.43
card/tmpsets/set/mod013-navbar/all/navbar_links.rb 93.33 % 67 30 28 2 321.63
card/tmpsets/set/mod013-navbar/right/enabled_roles.rb 77.14 % 70 35 27 8 61.91
card/tmpsets/set/mod013-navbar/self/account_links.rb 85.71 % 108 49 42 7 118.24
card/tmpsets/set/mod013-navbar/self/dropdown_divider.rb 88.89 % 18 9 8 1 26.00
card/tmpsets/set/mod013-navbar/self/navbox.rb 100.00 % 45 18 18 0 87.89
card/tmpsets/set/mod013-navbar/type/html.rb 63.64 % 23 11 7 4 0.91

Mod: core ( 78.95% covered at 3559.6 hits/line )

61 files in total.
2499 relevant lines, 1973 lines covered and 526 lines missed. ( 78.95% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/mod/core/chunk/escaped_literal.rb 100.00 % 27 10 10 0 2.40
card/mod/core/chunk/keep_escaped_literal.rb 100.00 % 26 10 10 0 2.40
card/mod/core/chunk/link.rb 75.81 % 121 62 47 15 678.23
card/mod/core/chunk/nest.rb 81.82 % 127 66 54 12 1493.77
card/mod/core/chunk/query_reference.rb 85.19 % 95 27 23 4 17.19
card/mod/core/chunk/reference.rb 73.33 % 59 30 22 8 1586.03
card/mod/core/chunk/uri.rb 86.79 % 145 53 46 7 2.66
card/mod/core/chunk/view_stub.rb 48.39 % 62 31 15 16 0.48
card/mod/core/format/data_format.rb 100.00 % 6 3 3 0 1.00
card/mod/core/format/html_format.rb 100.00 % 45 20 20 0 3412.00
card/mod/core/format/text_format.rb 87.50 % 16 8 7 1 0.88
card/mod/core/lib/card/fetch.rb 96.77 % 54 31 30 1 71676.81
card/mod/core/lib/card/fetch/results.rb 97.30 % 125 74 72 2 7958.54
card/mod/core/lib/card/fetch/retrieve.rb 100.00 % 56 30 30 0 30493.50
card/mod/core/lib/card/fetch/store.rb 100.00 % 66 31 31 0 39290.42
card/mod/core/lib/card/rule.rb 93.10 % 60 29 27 2 890.90
card/mod/core/lib/card/rule/cache.rb 100.00 % 69 29 29 0 6734.48
card/mod/core/lib/card/rule/preference_cache.rb 100.00 % 74 30 30 0 191.87
card/mod/core/lib/card/rule/read_rule_cache.rb 100.00 % 30 11 11 0 139.73
card/tmpsets/set/mod003-core/abstract/code_file.rb 40.00 % 93 50 20 30 1.78
card/tmpsets/set/mod003-core/abstract/haml_file.rb 58.82 % 33 17 10 7 0.76
card/tmpsets/set/mod003-core/abstract/lock.rb 47.37 % 35 19 9 10 0.58
card/tmpsets/set/mod003-core/abstract/vendor_code_file.rb 90.00 % 19 10 9 1 1.10
card/tmpsets/set/mod003-core/all/abort.rb 79.55 % 85 44 35 9 145.02
card/tmpsets/set/mod003-core/all/actify.rb 90.70 % 77 43 39 4 226.98
card/tmpsets/set/mod003-core/all/active_card.rb 100.00 % 15 7 7 0 160.43
card/tmpsets/set/mod003-core/all/assign_attributes.rb 93.67 % 135 79 74 5 2948.99
card/tmpsets/set/mod003-core/all/cache.rb 82.09 % 118 67 55 12 6451.36
card/tmpsets/set/mod003-core/all/chunk.rb 87.95 % 151 83 73 10 39.99
card/tmpsets/set/mod003-core/all/codename.rb 66.67 % 38 21 14 7 206.62
card/tmpsets/set/mod003-core/all/collection.rb 63.64 % 64 33 21 12 63.03
card/tmpsets/set/mod003-core/all/content.rb 81.52 % 187 92 75 17 375.76
card/tmpsets/set/mod003-core/all/contextual_content.rb 100.00 % 37 20 20 0 775.80
card/tmpsets/set/mod003-core/all/debug.rb 52.63 % 32 19 10 9 2.74
card/tmpsets/set/mod003-core/all/event_conditions.rb 84.78 % 181 92 78 14 5271.79
card/tmpsets/set/mod003-core/all/export.rb 40.00 % 77 40 16 24 0.48
card/tmpsets/set/mod003-core/all/extended.rb 92.00 % 58 25 23 2 1.04
card/tmpsets/set/mod003-core/all/fetch.rb 77.55 % 131 49 38 11 9021.39
card/tmpsets/set/mod003-core/all/fetch_helper.rb 100.00 % 44 22 22 0 230.41
card/tmpsets/set/mod003-core/all/haml.rb 75.86 % 84 29 22 7 349.83
card/tmpsets/set/mod003-core/all/i18n.rb 100.00 % 19 9 9 0 5.89
card/tmpsets/set/mod003-core/all/initialize.rb 100.00 % 85 49 49 0 5077.78
card/tmpsets/set/mod003-core/all/item.rb 68.83 % 139 77 53 24 32.42
card/tmpsets/set/mod003-core/all/layouts.rb 64.71 % 43 17 11 6 0.82
card/tmpsets/set/mod003-core/all/location_history.rb 85.71 % 28 14 12 2 24.14
card/tmpsets/set/mod003-core/all/name.rb 80.45 % 248 133 107 26 3077.98
card/tmpsets/set/mod003-core/all/name_events.rb 67.11 % 143 76 51 25 51.91
card/tmpsets/set/mod003-core/all/observer.rb 100.00 % 31 16 16 0 69.13
card/tmpsets/set/mod003-core/all/pattern.rb 93.94 % 63 33 31 2 8850.76
card/tmpsets/set/mod003-core/all/permissions.rb 85.60 % 242 125 107 18 448.68
card/tmpsets/set/mod003-core/all/references.rb 78.49 % 200 93 73 20 172.18
card/tmpsets/set/mod003-core/all/rename.rb 33.33 % 42 27 9 18 0.41
card/tmpsets/set/mod003-core/all/rules.rb 82.61 % 90 46 38 8 6818.00
card/tmpsets/set/mod003-core/all/states.rb 85.71 % 93 35 30 5 7981.26
card/tmpsets/set/mod003-core/all/subcards.rb 68.66 % 128 67 46 21 166.01
card/tmpsets/set/mod003-core/all/tabs.rb 40.00 % 60 30 12 18 0.50
card/tmpsets/set/mod003-core/all/templating.rb 95.00 % 80 40 38 2 2509.18
card/tmpsets/set/mod003-core/all/trash.rb 55.07 % 136 69 38 31 9.81
card/tmpsets/set/mod003-core/all/type.rb 88.24 % 70 34 30 4 1334.74
card/tmpsets/set/mod003-core/all/update_read_rules.rb 100.00 % 21 9 9 0 54.78
card/tmpsets/set/mod003-core/all/utils.rb 31.48 % 101 54 17 37 0.37

Mod: admin ( 43.07% covered at 3.78 hits/line )

5 files in total.
137 relevant lines, 59 lines covered and 78 lines missed. ( 43.07% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod002-admin/self/admin.rb 28.77 % 152 73 21 52 0.33
card/tmpsets/set/mod002-admin/self/admin_info.rb 52.00 % 57 25 13 12 0.64
card/tmpsets/set/mod002-admin/self/debugger.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod002-admin/self/trash.rb 48.00 % 72 25 12 13 0.60
card/tmpsets/set/mod002-admin/self/version.rb 100.00 % 22 8 8 0 57.00

Mod: standard ( 72.91% covered at 252.99 hits/line )

45 files in total.
1462 relevant lines, 1066 lines covered and 396 lines missed. ( 72.91% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/mod/standard/lib/card/layout.rb 76.92 % 93 52 40 12 40.50
card/mod/standard/lib/card/layout/card_layout.rb 78.26 % 44 23 18 5 20.52
card/tmpsets/set/mod010-standard/all/comment.rb 44.44 % 89 45 20 25 7.09
card/tmpsets/set/mod010-standard/all/error.rb 54.55 % 85 44 24 20 0.64
card/tmpsets/set/mod010-standard/all/links.rb 88.73 % 158 71 63 8 1129.61
card/tmpsets/set/mod010-standard/all/list_changes.rb 73.08 % 58 26 19 7 16.42
card/tmpsets/set/mod010-standard/all/path.rb 96.10 % 184 77 74 3 1787.64
card/tmpsets/set/mod010-standard/all/rich_html.rb 100.00 % 13 6 6 0 1.50
card/tmpsets/set/mod010-standard/all/rich_html/alert.rb 100.00 % 32 17 17 0 28.53
card/tmpsets/set/mod010-standard/all/rich_html/content.rb 63.92 % 177 97 62 35 32.51
card/tmpsets/set/mod010-standard/all/rich_html/error.rb 59.18 % 204 98 58 40 2.71
card/tmpsets/set/mod010-standard/all/rich_html/frame.rb 96.97 % 62 33 32 1 48.27
card/tmpsets/set/mod010-standard/all/rich_html/header.rb 77.78 % 70 36 28 8 75.64
card/tmpsets/set/mod010-standard/all/rich_html/html_views/guide.rb 81.82 % 60 33 27 6 32.30
card/tmpsets/set/mod010-standard/all/rich_html/html_views/help.rb 93.55 % 58 31 29 2 160.35
card/tmpsets/set/mod010-standard/all/rich_html/html_views/info.rb 38.10 % 93 42 16 26 6.38
card/tmpsets/set/mod010-standard/all/rich_html/html_views/size.rb 61.54 % 26 13 8 5 1.00
card/tmpsets/set/mod010-standard/all/rich_html/menu.rb 87.14 % 138 70 61 9 112.31
card/tmpsets/set/mod010-standard/all/rich_html/modal.rb 80.00 % 128 65 52 13 13.66
card/tmpsets/set/mod010-standard/all/rich_html/overlay.rb 37.10 % 113 62 23 39 0.44
card/tmpsets/set/mod010-standard/all/rich_html/process_layout.rb 93.02 % 80 43 40 3 108.79
card/tmpsets/set/mod010-standard/all/rich_html/show.rb 84.00 % 54 25 21 4 70.36
card/tmpsets/set/mod010-standard/all/rich_html/title.rb 89.29 % 56 28 25 3 490.86
card/tmpsets/set/mod010-standard/all/rich_html/wrapper.rb 86.59 % 156 82 71 11 1223.33
card/tmpsets/set/mod010-standard/right/discussion.rb 60.00 % 18 10 6 4 0.80
card/tmpsets/set/mod010-standard/right/head.rb 84.62 % 26 13 11 2 35.38
card/tmpsets/set/mod010-standard/right/type_plus_right.rb 83.33 % 13 6 5 1 1.17
card/tmpsets/set/mod010-standard/right/when_created.rb 71.43 % 15 7 5 2 1.00
card/tmpsets/set/mod010-standard/right/when_last_edited.rb 71.43 % 15 7 5 2 1.00
card/tmpsets/set/mod010-standard/self/alerts.rb 83.33 % 14 6 5 1 1.17
card/tmpsets/set/mod010-standard/self/cardtype.rb 52.63 % 51 19 10 9 3.05
card/tmpsets/set/mod010-standard/self/codenames.rb 100.00 % 10 4 4 0 1.50
card/tmpsets/set/mod010-standard/self/foot.rb 83.33 % 15 6 5 1 1.17
card/tmpsets/set/mod010-standard/self/home.rb 88.89 % 18 9 8 1 50.89
card/tmpsets/set/mod010-standard/self/now.rb 83.33 % 15 6 5 1 1.17
card/tmpsets/set/mod010-standard/self/sidebar.rb 83.33 % 14 6 5 1 1.17
card/tmpsets/set/mod010-standard/type/basic.rb 39.02 % 72 41 16 25 21.63
card/tmpsets/set/mod010-standard/type/cardtype.rb 71.62 % 136 74 53 21 2.64
card/tmpsets/set/mod010-standard/type/layout_type.rb 66.67 % 25 12 8 4 0.92
card/tmpsets/set/mod010-standard/type/notification_template.rb 60.87 % 42 23 14 9 0.74
card/tmpsets/set/mod010-standard/type/number.rb 53.33 % 31 15 8 7 0.73
card/tmpsets/set/mod010-standard/type/phrase.rb 100.00 % 14 7 7 0 15.43
card/tmpsets/set/mod010-standard/type/session.rb 69.44 % 70 36 25 11 70.92
card/tmpsets/set/mod010-standard/type/toggle.rb 75.00 % 47 24 18 6 1.00
card/tmpsets/set/mod010-standard/type/uri.rb 75.00 % 24 12 9 3 1.08

Mod: machines ( 56.96% covered at 8.94 hits/line )

22 files in total.
474 relevant lines, 270 lines covered and 204 lines missed. ( 56.96% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod007-card-mod-machines/abstract/machine.rb 45.05 % 230 111 50 61 16.98
card/tmpsets/set/mod007-card-mod-machines/abstract/machine/output_cache.rb 50.00 % 25 12 6 6 0.75
card/tmpsets/set/mod007-card-mod-machines/abstract/machine/output_update.rb 31.91 % 77 47 15 32 38.47
card/tmpsets/set/mod007-card-mod-machines/abstract/machine_input.rb 60.98 % 81 41 25 16 1.61
card/tmpsets/set/mod007-card-mod-machines/abstract/script.rb 56.82 % 93 44 25 19 0.75
card/tmpsets/set/mod007-card-mod-machines/abstract/skin_box.rb 66.67 % 42 21 14 7 1.71
card/tmpsets/set/mod007-card-mod-machines/all/reset_machines.rb 38.89 % 31 18 7 11 0.50
card/tmpsets/set/mod007-card-mod-machines/right/machine_cache.rb 85.71 % 14 7 6 1 1.14
card/tmpsets/set/mod007-card-mod-machines/right/machine_input.rb 75.00 % 16 8 6 2 1.00
card/tmpsets/set/mod007-card-mod-machines/right/machine_output.rb 44.44 % 53 27 12 15 0.59
card/tmpsets/set/mod007-card-mod-machines/self/script_decko.rb 75.00 % 21 8 6 2 1.00
card/tmpsets/set/mod007-card-mod-machines/self/script_html5shiv_printshiv.rb 100.00 % 20 8 8 0 29.25
card/tmpsets/set/mod007-card-mod-machines/self/script_jquery.rb 77.78 % 19 9 7 2 1.00
card/tmpsets/set/mod007-card-mod-machines/self/script_jquery_helper.rb 87.50 % 23 8 7 1 1.13
card/tmpsets/set/mod007-card-mod-machines/self/style_bootstrap_compatible.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod007-card-mod-machines/self/style_cards.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod007-card-mod-machines/self/style_jquery_ui_smoothness.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod007-card-mod-machines/type/coffee_script.rb 64.71 % 34 17 11 6 0.88
card/tmpsets/set/mod007-card-mod-machines/type/css.rb 59.52 % 94 42 25 17 0.71
card/tmpsets/set/mod007-card-mod-machines/type/java_script.rb 87.50 % 18 8 7 1 1.25
card/tmpsets/set/mod007-card-mod-machines/type/scss.rb 71.43 % 30 14 10 4 1.00
card/tmpsets/set/mod007-card-mod-machines/type/skin.rb 88.89 % 18 9 8 1 1.11

Mod: pointer ( 68.56% covered at 69.65 hits/line )

26 files in total.
738 relevant lines, 506 lines covered and 232 lines missed. ( 68.56% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod005-pointer/abstract/00_paging_params.rb 95.00 % 40 20 19 1 14.85
card/tmpsets/set/mod005-pointer/abstract/01_paging.rb 86.49 % 156 74 64 10 9.20
card/tmpsets/set/mod005-pointer/abstract/01_paging/paging_links.rb 100.00 % 98 43 43 0 3.74
card/tmpsets/set/mod005-pointer/abstract/02_items.rb 75.86 % 207 87 66 21 554.29
card/tmpsets/set/mod005-pointer/abstract/02_pointer.rb 90.00 % 19 10 9 1 1.50
card/tmpsets/set/mod005-pointer/abstract/02_pointer/events.rb 57.89 % 80 38 22 16 11.37
card/tmpsets/set/mod005-pointer/abstract/02_pointer/html_views.rb 65.57 % 134 61 40 21 5.25
card/tmpsets/set/mod005-pointer/abstract/02_pointer/html_views/filter.rb 46.88 % 71 32 15 17 0.66
card/tmpsets/set/mod005-pointer/abstract/02_pointer/options_api.rb 86.05 % 87 43 37 6 8.35
card/tmpsets/set/mod005-pointer/abstract/02_pointer/other_views.rb 62.00 % 111 50 31 19 1.70
card/tmpsets/set/mod005-pointer/abstract/code_pointer.rb 75.00 % 44 20 15 5 4.85
card/tmpsets/set/mod005-pointer/abstract/id_pointer.rb 50.00 % 30 14 7 7 0.64
card/tmpsets/set/mod005-pointer/right/content_options.rb 100.00 % 13 7 7 0 2.14
card/tmpsets/set/mod005-pointer/self/input_options.rb 92.86 % 22 14 13 1 1.07
card/tmpsets/set/mod005-pointer/self/script_editors.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/self/script_libraries.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/self/script_mods.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/self/script_pointer_config.rb 88.89 % 18 9 8 1 1.11
card/tmpsets/set/mod005-pointer/self/style_libraries.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/self/style_mods.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod005-pointer/type/link_list.rb 53.85 % 49 26 14 12 0.69
card/tmpsets/set/mod005-pointer/type/list.rb 76.92 % 25 13 10 3 41.46
card/tmpsets/set/mod005-pointer/type/mirror_list.rb 36.73 % 102 49 18 31 0.41
card/tmpsets/set/mod005-pointer/type/mirrored_list.rb 31.75 % 121 63 20 43 0.35
card/tmpsets/set/mod005-pointer/type/nest_list.rb 53.33 % 55 30 16 14 0.67
card/tmpsets/set/mod005-pointer/type/pointer.rb 70.00 % 19 10 7 3 1.00

Mod: Modfile ( 100.0% covered at 0.0 hits/line )

0 files in total.
0 relevant lines, 0 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line

Mod: utility ( 58.0% covered at 0.78 hits/line )

4 files in total.
50 relevant lines, 29 lines covered and 21 lines missed. ( 58.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod001-utility/abstract/bs_badge.rb 56.25 % 31 16 9 7 0.75
card/tmpsets/set/mod001-utility/abstract/filterable.rb 53.85 % 22 13 7 6 0.77
card/tmpsets/set/mod001-utility/abstract/filterable_bar.rb 63.64 % 20 11 7 4 0.82
card/tmpsets/set/mod001-utility/abstract/utility.rb 60.00 % 22 10 6 4 0.80

Mod: email ( 85.85% covered at 12.17 hits/line )

13 files in total.
205 relevant lines, 176 lines covered and 29 lines missed. ( 85.85% )

Mod: monkey ( 33.01% covered at 0.41 hits/line )

3 files in total.
103 relevant lines, 34 lines covered and 69 lines missed. ( 33.01% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod030-card-mod-monkey/all/event_viz.rb 33.33 % 73 36 12 24 0.39
card/tmpsets/set/mod030-card-mod-monkey/all/view_viz.rb 38.89 % 32 18 7 11 0.56
card/tmpsets/set/mod030-card-mod-monkey/right/debug.rb 30.61 % 97 49 15 34 0.37

Mod: delayed_job ( 100.0% covered at 1.0 hits/line )

1 files in total.
1 relevant lines, 1 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card-mod-delayed_job/lib/card/mod/delayed_job.rb 100.00 % 1 1 1 0 1.00

Mod: bar_and_box ( 82.76% covered at 11.23 hits/line )

5 files in total.
116 relevant lines, 96 lines covered and 20 lines missed. ( 82.76% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod016-card-mod-bar_and_box/abstract/media.rb 100.00 % 26 14 14 0 3.36
card/tmpsets/set/mod016-card-mod-bar_and_box/all/bar.rb 78.33 % 114 60 47 13 19.40
card/tmpsets/set/mod016-card-mod-bar_and_box/all/box.rb 66.67 % 25 12 8 4 0.92
card/tmpsets/set/mod016-card-mod-bar_and_box/self/style_media.rb 87.50 % 15 8 7 1 1.13
card/tmpsets/set/mod016-card-mod-bar_and_box/type/image.rb 90.91 % 47 22 20 2 3.27

Mod: format ( 67.82% covered at 77.89 hits/line )

13 files in total.
435 relevant lines, 295 lines covered and 140 lines missed. ( 67.82% )

Mod: ace_editor ( 96.67% covered at 1.43 hits/line )

4 files in total.
30 relevant lines, 29 lines covered and 1 lines missed. ( 96.67% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod015-card-mod-ace_editor/all/ace_editor.rb 100.00 % 21 9 9 0 1.78
card/tmpsets/set/mod015-card-mod-ace_editor/self/ace.rb 85.71 % 16 7 6 1 1.29
card/tmpsets/set/mod015-card-mod-ace_editor/self/script_ace.rb 100.00 % 13 7 7 0 1.29
card/tmpsets/set/mod015-card-mod-ace_editor/self/script_ace_config.rb 100.00 % 13 7 7 0 1.29

Mod: carrierwave ( 75.38% covered at 145.92 hits/line )

16 files in total.
597 relevant lines, 450 lines covered and 147 lines missed. ( 75.38% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment.rb 78.13 % 121 64 50 14 46.44
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/cloud.rb 36.99 % 142 73 27 46 8.07
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/coded.rb 56.25 % 31 16 9 7 5.88
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/local.rb 82.14 % 49 28 23 5 3.43
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/paths.rb 91.43 % 67 35 32 3 620.69
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/storage_type.rb 70.21 % 179 94 66 28 544.70
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/upload_cache.rb 100.00 % 94 49 49 0 9.12
card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/web.rb 100.00 % 12 6 6 0 3.00
card/tmpsets/set/mod019-card-mod-carrierwave/all/file_utils.rb 90.91 % 50 22 20 2 1.55
card/tmpsets/set/mod019-card-mod-carrierwave/self/admin.rb 60.00 % 32 10 6 4 0.80
card/tmpsets/set/mod019-card-mod-carrierwave/self/favicon.rb 92.31 % 25 13 12 1 69.77
card/tmpsets/set/mod019-card-mod-carrierwave/self/new_file.rb 72.73 % 22 11 8 3 1.00
card/tmpsets/set/mod019-card-mod-carrierwave/self/new_image.rb 90.91 % 22 11 10 1 1.73
card/tmpsets/set/mod019-card-mod-carrierwave/type/file.rb 90.91 % 125 66 60 6 41.17
card/tmpsets/set/mod019-card-mod-carrierwave/type/image.rb 78.33 % 110 60 47 13 96.23
card/tmpsets/set/mod019-card-mod-carrierwave/type/image/html_views.rb 64.10 % 78 39 25 14 12.90

Mod: search ( 69.68% covered at 15.19 hits/line )

25 files in total.
620 relevant lines, 432 lines covered and 188 lines missed. ( 69.68% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod009-card-mod-search/abstract/00_filter_helper.rb 83.33 % 64 30 25 5 62.70
card/tmpsets/set/mod009-card-mod-search/abstract/02_search_params.rb 90.91 % 67 33 30 3 8.24
card/tmpsets/set/mod009-card-mod-search/abstract/03_filter.rb 75.00 % 32 16 12 4 1.06
card/tmpsets/set/mod009-card-mod-search/abstract/03_filter/filter_form.rb 44.44 % 107 45 20 25 0.53
card/tmpsets/set/mod009-card-mod-search/abstract/03_filter/form_helper.rb 35.94 % 114 64 23 41 0.42
card/tmpsets/set/mod009-card-mod-search/abstract/03_filter/query_construction.rb 68.18 % 45 22 15 7 34.36
card/tmpsets/set/mod009-card-mod-search/abstract/04_right_filter_form.rb 68.75 % 35 16 11 5 0.88
card/tmpsets/set/mod009-card-mod-search/abstract/05_search.rb 76.47 % 93 51 39 12 5.63
card/tmpsets/set/mod009-card-mod-search/abstract/05_search/views.rb 60.47 % 168 86 52 34 2.81
card/tmpsets/set/mod009-card-mod-search/abstract/06_cql_search.rb 93.65 % 117 63 59 4 80.92
card/tmpsets/set/mod009-card-mod-search/right/children.rb 83.33 % 13 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/created.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/edited.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/editors.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/follow.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/linked_to_by.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/links_to.rb 83.33 % 14 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/mates.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/nested_by.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/nests.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/referred_to_by.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/right/refers_to.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod009-card-mod-search/self/recent.rb 66.67 % 44 24 16 8 19.46
card/tmpsets/set/mod009-card-mod-search/self/search.rb 86.54 % 105 52 45 7 2.44
card/tmpsets/set/mod009-card-mod-search/type/search_type.rb 54.35 % 89 46 25 21 2.70

Mod: rules ( 53.4% covered at 6.86 hits/line )

20 files in total.
588 relevant lines, 314 lines covered and 274 lines missed. ( 53.4% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod027-card-mod-rules/right/self.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod027-card-mod-rules/rstar/rule_user.rb 87.50 % 17 8 7 1 4.13
card/tmpsets/set/mod027-card-mod-rules/rule/bar_view.rb 69.39 % 101 49 34 15 0.82
card/tmpsets/set/mod027-card-mod-rules/rule/bridge_rules_editor.rb 72.22 % 37 18 13 5 3.89
card/tmpsets/set/mod027-card-mod-rules/rule/editor.rb 48.15 % 119 54 26 28 0.59
card/tmpsets/set/mod027-card-mod-rules/rule/html_views.rb 88.89 % 22 9 8 1 16.33
card/tmpsets/set/mod027-card-mod-rules/rule/quick_editor.rb 57.14 % 48 21 12 9 0.71
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form.rb 37.14 % 66 35 13 22 0.46
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/buttons.rb 56.52 % 55 23 13 10 0.87
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/form_elements.rb 33.33 % 61 33 11 22 0.45
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/rule_set_radio.rb 36.17 % 91 47 17 30 0.40
card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/set_selection.rb 39.13 % 48 23 9 14 0.57
card/tmpsets/set/mod027-card-mod-rules/rule/rules.rb 52.54 % 118 59 31 28 3.69
card/tmpsets/set/mod027-card-mod-rules/self/script_rules.rb 100.00 % 12 6 6 0 1.33
card/tmpsets/set/mod027-card-mod-rules/type/set.rb 56.45 % 116 62 35 27 51.56
card/tmpsets/set/mod027-card-mod-rules/type/set/html_views.rb 58.62 % 60 29 17 12 0.93
card/tmpsets/set/mod027-card-mod-rules/type/set/html_views/rule_lists.rb 50.00 % 51 26 13 13 0.69
card/tmpsets/set/mod027-card-mod-rules/type/set/html_views/template.rb 100.00 % 21 11 11 0 8.73
card/tmpsets/set/mod027-card-mod-rules/type/set/rules_filter.rb 46.43 % 69 28 13 15 0.61
card/tmpsets/set/mod027-card-mod-rules/type/set/setting_lists.rb 48.78 % 85 41 20 21 0.59

Mod: defaults ( 100.0% covered at 0.0 hits/line )

0 files in total.
0 relevant lines, 0 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line

Mod: bootstrap ( 59.78% covered at 30.74 hits/line )

30 files in total.
644 relevant lines, 385 lines covered and 259 lines missed. ( 59.78% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod017-card-mod-bootstrap/abstract/bootstrap_code_file.rb 54.29 % 66 35 19 16 0.71
card/tmpsets/set/mod017-card-mod-bootstrap/abstract/bootswatch_theme.rb 53.57 % 166 56 30 26 0.57
card/tmpsets/set/mod017-card-mod-bootstrap/abstract/bootswatch_theme/html_views.rb 80.00 % 39 20 16 4 2.20
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/accordion.rb 28.57 % 72 28 8 20 0.43
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/dropdown.rb 82.35 % 95 34 28 6 185.65
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/form.rb 100.00 % 44 23 23 0 115.91
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/helper.rb 40.00 % 62 40 16 24 25.90
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/icon.rb 91.18 % 118 34 31 3 267.29
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/navbar.rb 33.33 % 75 33 11 22 0.45
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/table.rb 29.09 % 108 55 16 39 0.36
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/tabs.rb 100.00 % 49 9 9 0 1.78
card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/wrapper.rb 93.33 % 28 15 14 1 21.60
card/tmpsets/set/mod017-card-mod-bootstrap/all/rich_bootstrap.rb 66.67 % 24 12 8 4 0.92
card/tmpsets/set/mod017-card-mod-bootstrap/self/bootstrap_core.rb 54.55 % 22 11 6 5 0.73
card/tmpsets/set/mod017-card-mod-bootstrap/self/bootstrap_functions.rb 85.71 % 14 7 6 1 1.14
card/tmpsets/set/mod017-card-mod-bootstrap/self/font_awesome.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod017-card-mod-bootstrap/self/material_icons.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod017-card-mod-bootstrap/self/script_bootstrap.rb 80.00 % 21 10 8 2 1.00
card/tmpsets/set/mod017-card-mod-bootstrap/self/script_load_select2.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod017-card-mod-bootstrap/self/script_select2.rb 80.00 % 21 10 8 2 1.00
card/tmpsets/set/mod017-card-mod-bootstrap/self/smartmenu_css.rb 85.71 % 14 7 6 1 1.14
card/tmpsets/set/mod017-card-mod-bootstrap/self/smartmenu_js.rb 85.71 % 14 7 6 1 1.14
card/tmpsets/set/mod017-card-mod-bootstrap/self/style_bootstrap_cards.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod017-card-mod-bootstrap/self/style_bootstrap_colorpicker.rb 87.50 % 15 8 7 1 1.13
card/tmpsets/set/mod017-card-mod-bootstrap/self/style_select2.rb 80.00 % 19 10 8 2 1.00
card/tmpsets/set/mod017-card-mod-bootstrap/self/style_select2_bootstrap.rb 100.00 % 10 5 5 0 1.40
card/tmpsets/set/mod017-card-mod-bootstrap/type/bootswatch_skin.rb 77.78 % 24 9 7 2 1.00
card/tmpsets/set/mod017-card-mod-bootstrap/type/customized_bootswatch_skin.rb 41.18 % 124 68 28 40 0.46
card/tmpsets/set/mod017-card-mod-bootstrap/type/customized_bootswatch_skin/html_views.rb 65.22 % 45 23 15 8 0.87
card/tmpsets/set/mod017-card-mod-bootstrap/type_plus_right/customized_bootswatch_skin/colors.rb 44.23 % 122 52 23 29 0.56

Mod: date ( 86.96% covered at 1.17 hits/line )

6 files in total.
46 relevant lines, 40 lines covered and 6 lines missed. ( 86.96% )

Mod: google_analytics ( 83.33% covered at 55.04 hits/line )

1 files in total.
24 relevant lines, 20 lines covered and 4 lines missed. ( 83.33% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod023-card-mod-google_analytics/all/google_analytics.rb 83.33 % 51 24 20 4 55.04

Mod: markdown ( 84.62% covered at 8.62 hits/line )

1 files in total.
13 relevant lines, 11 lines covered and 2 lines missed. ( 84.62% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod024-card-mod-markdown/type/markdown.rb 84.62 % 26 13 11 2 8.62

Mod: edit ( 75.36% covered at 55.36 hits/line )

22 files in total.
755 relevant lines, 569 lines covered and 186 lines missed. ( 75.36% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod014-card-mod-edit/all/bridge.rb 44.12 % 73 34 15 19 0.53
card/tmpsets/set/mod014-card-mod-edit/all/bridge/bridge_pills.rb 38.71 % 56 31 12 19 0.52
card/tmpsets/set/mod014-card-mod-edit/all/bridge/follow_section.rb 47.62 % 46 21 10 11 0.67
card/tmpsets/set/mod014-card-mod-edit/all/bridge/related_section.rb 56.25 % 43 16 9 7 0.81
card/tmpsets/set/mod014-card-mod-edit/all/bridge/tab_views.rb 46.15 % 50 26 12 14 0.62
card/tmpsets/set/mod014-card-mod-edit/all/bridge/tab_visibility.rb 50.00 % 62 32 16 16 0.63
card/tmpsets/set/mod014-card-mod-edit/all/edit_content.rb 100.00 % 69 28 28 0 13.86
card/tmpsets/set/mod014-card-mod-edit/all/edit_inline.rb 75.00 % 65 28 21 7 31.61
card/tmpsets/set/mod014-card-mod-edit/all/edit_name.rb 45.16 % 67 31 14 17 0.55
card/tmpsets/set/mod014-card-mod-edit/all/edit_type.rb 77.78 % 110 54 42 12 10.89
card/tmpsets/set/mod014-card-mod-edit/all/editing.rb 56.25 % 100 32 18 14 2.53
card/tmpsets/set/mod014-card-mod-edit/all/editor.rb 90.00 % 65 30 27 3 44.60
card/tmpsets/set/mod014-card-mod-edit/all/form.rb 94.21 % 254 121 114 7 202.56
card/tmpsets/set/mod014-card-mod-edit/all/form_buttons.rb 88.10 % 73 42 37 5 12.24
card/tmpsets/set/mod014-card-mod-edit/all/form_elements.rb 92.86 % 80 42 39 3 162.95
card/tmpsets/set/mod014-card-mod-edit/all/formgroup.rb 100.00 % 46 27 27 0 164.78
card/tmpsets/set/mod014-card-mod-edit/all/new.rb 79.79 % 197 94 75 19 18.29
card/tmpsets/set/mod014-card-mod-edit/all/overlay_guide.rb 100.00 % 18 6 6 0 1.67
card/tmpsets/set/mod014-card-mod-edit/all/template_nest.rb 88.89 % 51 27 24 3 12.11
card/tmpsets/set/mod014-card-mod-edit/type/list.rb 66.67 % 24 12 8 4 0.83
card/tmpsets/set/mod014-card-mod-edit/type/plain_text.rb 72.73 % 22 11 8 3 1.00
card/tmpsets/set/mod014-card-mod-edit/type/pointer.rb 70.00 % 20 10 7 3 0.90

Mod: history ( 67.04% covered at 58.8 hits/line )

10 files in total.
446 relevant lines, 299 lines covered and 147 lines missed. ( 67.04% )

Mod: virtual ( 54.17% covered at 0.63 hits/line )

1 files in total.
24 relevant lines, 13 lines covered and 11 lines missed. ( 54.17% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod006-card-mod-virtual/abstract/virtual_cache.rb 54.17 % 52 24 13 11 0.63

Mod: recaptcha ( 61.86% covered at 28.89 hits/line )

6 files in total.
97 relevant lines, 60 lines covered and 37 lines missed. ( 61.86% )

Mod: account ( 81.96% covered at 21.12 hits/line )

18 files in total.
593 relevant lines, 486 lines covered and 107 lines missed. ( 81.96% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod012-card-mod-account/abstract/account_field.rb 100.00 % 26 11 11 0 3.64
card/tmpsets/set/mod012-card-mod-account/abstract/accountable.rb 72.00 % 58 25 18 7 5.56
card/tmpsets/set/mod012-card-mod-account/all/account.rb 90.74 % 106 54 49 5 156.04
card/tmpsets/set/mod012-card-mod-account/right/account.rb 91.43 % 67 35 32 3 6.74
card/tmpsets/set/mod012-card-mod-account/right/account/events.rb 86.54 % 107 52 45 7 1.17
card/tmpsets/set/mod012-card-mod-account/right/account/views.rb 88.57 % 74 35 31 4 1.69
card/tmpsets/set/mod012-card-mod-account/right/api_key.rb 57.14 % 57 14 8 6 0.71
card/tmpsets/set/mod012-card-mod-account/right/email.rb 81.48 % 52 27 22 5 4.48
card/tmpsets/set/mod012-card-mod-account/right/password.rb 96.55 % 60 29 28 1 15.24
card/tmpsets/set/mod012-card-mod-account/right/roles.rb 94.74 % 36 19 18 1 1.16
card/tmpsets/set/mod012-card-mod-account/right/salt.rb 90.91 % 22 11 10 1 1.45
card/tmpsets/set/mod012-card-mod-account/right/status.rb 78.57 % 27 14 11 3 0.93
card/tmpsets/set/mod012-card-mod-account/self/signin.rb 80.67 % 235 119 96 23 23.62
card/tmpsets/set/mod012-card-mod-account/type/role.rb 60.00 % 30 15 9 6 0.80
card/tmpsets/set/mod012-card-mod-account/type/signup.rb 78.13 % 63 32 25 7 0.88
card/tmpsets/set/mod012-card-mod-account/type/signup/views.rb 54.55 % 102 55 30 25 0.65
card/tmpsets/set/mod012-card-mod-account/type/user.rb 100.00 % 77 36 36 0 1.22
card/tmpsets/set/mod012-card-mod-account/type_plus_right/user/email.rb 70.00 % 22 10 7 3 1.00

Mod: prosemirror_editor ( 92.86% covered at 1.25 hits/line )

4 files in total.
28 relevant lines, 26 lines covered and 2 lines missed. ( 92.86% )

Mod: follow ( 69.24% covered at 24.72 hits/line )

27 files in total.
725 relevant lines, 502 lines covered and 223 lines missed. ( 69.24% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card/tmpsets/set/mod022-card-mod-follow/abstract/follow_option.rb 93.10 % 65 29 27 2 3.14
card/tmpsets/set/mod022-card-mod-follow/all/follow.rb 70.00 % 42 20 14 6 5.35
card/tmpsets/set/mod022-card-mod-follow/all/follow/follow_link.rb 50.00 % 69 30 15 15 0.57
card/tmpsets/set/mod022-card-mod-follow/all/follow/follow_link_views.rb 68.42 % 38 19 13 6 1.00
card/tmpsets/set/mod022-card-mod-follow/all/follow/followed_by.rb 73.68 % 79 38 28 10 185.47
card/tmpsets/set/mod022-card-mod-follow/all/follow/follower_ids.rb 92.75 % 130 69 64 5 64.90
card/tmpsets/set/mod022-card-mod-follow/all/follow/start_follow_link.rb 50.00 % 19 10 5 5 0.70
card/tmpsets/set/mod022-card-mod-follow/all/follow/stop_follow_link.rb 45.45 % 20 11 5 6 0.64
card/tmpsets/set/mod022-card-mod-follow/all/notify.rb 95.45 % 90 44 42 2 60.89
card/tmpsets/set/mod022-card-mod-follow/all/notify/base_views.rb 94.37 % 136 71 67 4 29.39
card/tmpsets/set/mod022-card-mod-follow/all/notify/html_views.rb 100.00 % 26 11 11 0 6.64
card/tmpsets/set/mod022-card-mod-follow/right/account.rb 100.00 % 24 11 11 0 8.45
card/tmpsets/set/mod022-card-mod-follow/right/follow.rb 55.00 % 127 60 33 27 2.50
card/tmpsets/set/mod022-card-mod-follow/right/follow_fields.rb 83.33 % 12 6 5 1 1.17
card/tmpsets/set/mod022-card-mod-follow/right/followers.rb 92.86 % 32 14 13 1 3.50
card/tmpsets/set/mod022-card-mod-follow/right/following.rb 43.33 % 59 30 13 17 0.53
card/tmpsets/set/mod022-card-mod-follow/self/always.rb 81.82 % 22 11 9 2 3.55
card/tmpsets/set/mod022-card-mod-follow/self/created.rb 78.57 % 28 14 11 3 24.79
card/tmpsets/set/mod022-card-mod-follow/self/edited.rb 78.57 % 29 14 11 3 24.79
card/tmpsets/set/mod022-card-mod-follow/self/follow.rb 100.00 % 11 6 6 0 1.33
card/tmpsets/set/mod022-card-mod-follow/self/follow_defaults.rb 35.90 % 97 39 14 25 0.44
card/tmpsets/set/mod022-card-mod-follow/self/never.rb 81.82 % 22 11 9 2 1.18
card/tmpsets/set/mod022-card-mod-follow/type/cardtype.rb 66.67 % 30 15 10 5 0.87
card/tmpsets/set/mod022-card-mod-follow/type/set.rb 64.52 % 62 31 20 11 4.74
card/tmpsets/set/mod022-card-mod-follow/type/user.rb 55.56 % 16 9 5 4 0.78
card/tmpsets/set/mod022-card-mod-follow/type_plus_right/user/follow.rb 52.38 % 86 42 22 20 0.79
card/tmpsets/set/mod022-card-mod-follow/type_plus_right/user/follow/follow_editor_helper.rb 31.67 % 130 60 19 41 0.40

Mod: platypus ( 100.0% covered at 1.0 hits/line )

1 files in total.
1 relevant lines, 1 lines covered and 0 lines missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card-mod-platypus/lib/card/mod/platypus.rb 100.00 % 1 1 1 0 1.00

Ungrouped ( 78.04% covered at 3169.95 hits/line )

60 files in total.
1630 relevant lines, 1272 lines covered and 358 lines missed. ( 78.04% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
card-mod-bootstrap/lib/bootstrap.rb 90.00 % 16 10 9 1 1.20
card-mod-bootstrap/lib/bootstrap/basic_tags.rb 40.00 % 26 15 6 9 0.40
card-mod-bootstrap/lib/bootstrap/component.rb 33.03 % 197 109 36 73 0.54
card-mod-bootstrap/lib/bootstrap/component/carousel.rb 27.50 % 68 40 11 29 0.28
card-mod-bootstrap/lib/bootstrap/component/form.rb 41.67 % 65 24 10 14 0.96
card-mod-bootstrap/lib/bootstrap/component/horizontal_form.rb 44.83 % 63 29 13 16 0.45
card-mod-bootstrap/lib/bootstrap/component/layout.rb 85.00 % 95 40 34 6 2.50
card-mod-bootstrap/lib/bootstrap/component/panel.rb 100.00 % 9 6 6 0 1.00
card-mod-bootstrap/lib/bootstrap/component_loader.rb 100.00 % 28 16 16 0 3.25
card-mod-bootstrap/lib/bootstrap/delegate.rb 75.00 % 16 8 6 2 27.50
card-mod-bootstrap/lib/bootstrap/old_component.rb 81.00 % 182 100 81 19 8.20
card-mod-bootstrap/lib/bootstrapper.rb 88.89 % 16 9 8 1 1.89
card-mod-bootstrap/lib/card/lazy_tab.rb 95.00 % 37 20 19 1 3.05
card-mod-bootstrap/lib/card/tab.rb 97.50 % 76 40 39 1 3.18
card-mod-carrierwave/lib/carrier_wave/card_mount.rb 100.00 % 134 12 12 0 1.42
card-mod-carrierwave/lib/carrier_wave/file_card_uploader.rb 89.77 % 320 88 79 9 653.31
card-mod-carrierwave/lib/carrier_wave/image_card_uploader.rb 100.00 % 59 22 22 0 343.09
card-mod-delayed_job/lib/cardio/delayed_job.rb 100.00 % 9 5 5 0 7.80
card-mod-email/format/email_html_format.rb 83.33 % 14 6 5 1 0.83
card-mod-email/format/email_text_format.rb 100.00 % 12 5 5 0 10.40
card-mod-follow/lib/card/follow_option.rb 80.00 % 33 15 12 3 1.27
card-mod-follow/lib/card/follower_stash.rb 97.83 % 88 46 45 1 62.20
card-mod-format/format/css_format.rb 75.00 % 17 8 6 2 0.75
card-mod-format/format/csv_format.rb 75.00 % 19 8 6 2 0.75
card-mod-format/format/file_format.rb 100.00 % 8 3 3 0 1.00
card-mod-format/format/js_format.rb 75.00 % 17 8 6 2 0.75
card-mod-format/format/json_format.rb 63.64 % 24 11 7 4 1.55
card-mod-format/format/rss_format.rb 100.00 % 9 4 4 0 1.00
card-mod-format/format/xml_format.rb 83.33 % 13 6 5 1 0.83
card-mod-history/lib/card/act.rb 75.00 % 137 44 33 11 31.68
card-mod-history/lib/card/act/act_renderer.rb 89.89 % 217 89 80 9 2.76
card-mod-history/lib/card/act/act_renderer/absolute_act_renderer.rb 100.00 % 34 13 13 0 1.38
card-mod-history/lib/card/action.rb 91.14 % 230 79 72 7 80.05
card-mod-history/lib/card/action/action_renderer.rb 87.23 % 94 47 41 6 2.26
card-mod-history/lib/card/action/admin.rb 31.58 % 36 19 6 13 0.32
card-mod-history/lib/card/action/differ.rb 82.50 % 89 40 33 7 4.98
card-mod-history/lib/card/change.rb 77.78 % 70 18 14 4 23.50
card-mod-machines/lib/card/machine.rb 57.89 % 33 19 11 8 65.79
card/lib/application_job.rb 100.00 % 2 1 1 0 1.00
card/lib/application_record.rb 100.00 % 3 2 2 0 1.00
card/tmpsets/set_pattern/100-all.rb 81.82 % 23 11 9 2 13.45
card/tmpsets/set_pattern/101-all_plus.rb 75.00 % 25 12 9 3 0.75
card/tmpsets/set_pattern/102-type.rb 95.00 % 41 20 19 1 1029.00
card/tmpsets/set_pattern/103-star.rb 76.92 % 27 13 10 3 591.00
card/tmpsets/set_pattern/104-rstar.rb 85.71 % 29 14 12 2 548.93
card/tmpsets/set_pattern/105-rule.rb 85.71 % 29 14 12 2 549.64
card/tmpsets/set_pattern/106-right.rb 81.25 % 36 16 13 3 278.19
card/tmpsets/set_pattern/107-type_plus_right.rb 77.78 % 44 18 14 4 244.39
card/tmpsets/set_pattern/108-self.rb 94.12 % 35 17 16 1 911.00
cardname/lib/cardname.rb 90.24 % 152 82 74 8 53327.32
cardname/lib/cardname/contextual.rb 82.43 % 128 74 61 13 3503.82
cardname/lib/cardname/manipulate.rb 32.61 % 78 46 15 31 40.85
cardname/lib/cardname/parts.rb 72.41 % 139 58 42 16 5964.95
cardname/lib/cardname/predicates.rb 76.47 % 30 17 13 4 226.53
cardname/lib/cardname/variants.rb 100.00 % 36 16 16 0 1658.25
decko-cucumber/lib/decko/cucumber.rb 100.00 % 9 9 9 0 1.00
decko-spring/lib/decko/spring.rb 100.00 % 4 3 3 0 1.00
decko/rails/controllers/application_controller.rb 100.00 % 2 1 1 0 1.00
decko/rails/controllers/card_controller.rb 96.15 % 151 78 75 3 114.24
decko/rails/engine-routes.rb 100.00 % 53 27 27 0 1.22

card-mod-bootstrap/lib/bootstrap.rb

90.0% lines covered

10 relevant lines. 9 lines covered and 1 lines missed.
    
  1. 1 require "bootstrap/component_loader"
  2. 1 require "bootstrap/component"
  3. 1 class Bootstrap
  4. 1 include Delegate
  5. 1 extend ComponentLoader
  6. 1 load_components
  7. 1 def initialize context=nil
  8. 4 @context = context
  9. end
  10. 1 def render *args, &block
  11. instance_exec *args, &block
  12. end
  13. end

card-mod-bootstrap/lib/bootstrap/basic_tags.rb

40.0% lines covered

15 relevant lines. 6 lines covered and 9 lines missed.
    
  1. # require 'component'
  2. 1 class Bootstrap
  3. 1 module BasicTags
  4. 1 def html content
  5. add_content String(content).html_safe
  6. ""
  7. end
  8. 1 Component.def_div_method :div, nil do |opts, extra_args|
  9. prepend_class opts, extra_args.first if extra_args.present?
  10. opts
  11. end
  12. 1 Component.def_div_method :span, nil do |opts, extra_args|
  13. prepend_class opts, extra_args.first if extra_args.present?
  14. opts
  15. end
  16. 1 Component.def_tag_method :tag, nil, tag: :yield do |opts, extra_args|
  17. prepend_class opts, extra_args[1] if extra_args[1].present?
  18. opts[:tag] = extra_args[0]
  19. opts
  20. end
  21. end
  22. end

card-mod-bootstrap/lib/bootstrap/component.rb

33.03% lines covered

109 relevant lines. 36 lines covered and 73 lines missed.
    
  1. #! no set module
  2. 1 class Bootstrap
  3. 1 class TagMethod
  4. 1 def initialize component, name, html_class, tag_opts={}, &tag_block
  5. @component = component
  6. @name = name
  7. @html_class = html_class
  8. @tag_opts = tag_opts
  9. @tag_block = tag_block
  10. @append = []
  11. @wrap = []
  12. @xm = Builder::XmlMarkup.new
  13. end
  14. 1 def call *args, &content_block
  15. component.content.push "".html_safe
  16. content, opts = content_block.call
  17. wrappers = @wrap.pop
  18. if wrappers.present?
  19. while wrappers.present? do
  20. wrapper = wrappers.shift
  21. if wrapper.is_a? Symbol
  22. send wrapper, &content_block
  23. else
  24. instance_exec(content, &wrappers.shift)
  25. end
  26. end
  27. else
  28. add_content content
  29. end
  30. collected_content = @content.pop
  31. tag_name = opts.delete(:tag) if tag_name == :yield
  32. add_content content_tag(tag_name, collected_content, opts, false)
  33. @append.pop.each do |block|
  34. add_content instance_exec(&block)
  35. end
  36. ""
  37. end
  38. 1 def method_missing method, *args, &block
  39. @component.send method, *args, &block
  40. end
  41. 1 def prepend &block
  42. tmp = @content.pop
  43. instance_exec &block
  44. @content << tmp
  45. end
  46. 1 def wrap &block
  47. instance_exec &block
  48. end
  49. 1 def append &block
  50. @append[-1] << block
  51. end
  52. 1 def wrapInner tag=nil, &block
  53. @wrap[-1] << (block_given? ? block : tag)
  54. end
  55. end
  56. 1 class Component
  57. 1 def initialize context, *args, &block
  58. @context = context
  59. @content = ["".html_safe]
  60. @args = args
  61. @child_args = []
  62. @append = []
  63. @wrap = []
  64. @build_block = block
  65. @html = Builder::XmlMarkup.new
  66. end
  67. 1 class << self
  68. 1 def render format, *args, &block
  69. new(format, *args, &block).render
  70. end
  71. # Like def_tag_method but always generates a div tag
  72. # The tag option is not available
  73. 1 def def_div_method name, html_class, opts={}, &tag_block
  74. 4 def_tag_method name, html_class, opts.merge(tag: :div), &tag_block
  75. end
  76. # Defines a method that generates a html tag
  77. # @param method_name [Symbol, String] the name of the method. If no :tag option in tag_opts is defined then the name is also the name of the tag that the method generates
  78. # @param html_class [String] a html class that is added to tag. Use nil if you don't want a html_class
  79. # @param tag_opts [Hash] additional argument that will be added to the tag
  80. # @option tag_opts [Symbol, String] tag the name of the tag
  81. # @example
  82. # def_tag_method :link, "known-link", tag: :a, id: "uniq-link"
  83. # link # => <a class="known-link" id="uniq-link"></a>
  84. 1 def def_tag_method method_name, html_class, tag_opts={}, &tag_opts_block
  85. 8 tag = tag_opts.delete(:tag) || method_name
  86. 8 return def_simple_tag_method method_name, tag, html_class, tag_opts unless block_given?
  87. 5 define_method method_name do |*args, &content_block|
  88. content, opts, new_child_args = standardize_args args, &tag_opts_block
  89. add_classes opts, html_class, tag_opts.delete(:optional_classes)
  90. @html.tag! tag, opts do
  91. instance_exec &content_block
  92. end
  93. end
  94. end
  95. 1 def def_simple_tag_method method_name, tag, html_class, tag_opts={}
  96. 3 define_method method_name do |*args, &content_block|
  97. @html.tag! tag, class: html_class do
  98. instance_exec &content_block
  99. end
  100. end
  101. end
  102. end
  103. 1 def render
  104. @rendered = begin
  105. render_content
  106. # @content[-1]
  107. end
  108. end
  109. 1 def prepend &block
  110. tmp = @content.pop
  111. instance_exec &block
  112. @content << tmp
  113. end
  114. 1 def insert &block
  115. instance_exec &block
  116. end
  117. 1 def append &block
  118. @append[-1] << block
  119. end
  120. 1 def wrap tag=nil, &block
  121. @wrap[-1] << (block_given? ? block : tag)
  122. end
  123. 1 private
  124. 1 def render_content
  125. # if @build_block.arity > 0
  126. instance_exec *@args, &@build_block
  127. end
  128. 1 def generate_content content, processor, &block
  129. content = instance_exec &block if block.present?
  130. return content if !processor || !content.is_a?(Array)
  131. content.each {|item| send processor, item}
  132. ""
  133. end
  134. 1 def with_child_args args
  135. @child_args << args if args.present?
  136. res = yield
  137. @child_args.pop if args.present?
  138. res
  139. end
  140. 1 def add_content content
  141. @content[-1] << "\n#{content}".html_safe if content.present?
  142. end
  143. 1 def process_tag tag_name, &content_block
  144. end
  145. 1 def standardize_args args, &block
  146. opts = args.last.is_a?(Hash) ? args.pop : {}
  147. items = ((args.one? && args.last.is_a?(String)) || args.last.is_a?(Array)) &&
  148. args.pop
  149. if block.present?
  150. opts, args = instance_exec opts, args, &block
  151. unless opts.is_a?(Hash)
  152. raise Card::Error, "first return value of a tag block has to be a hash"
  153. end
  154. end
  155. [items, opts, args]
  156. end
  157. 1 def add_classes opts, html_class, optional_classes
  158. prepend_class opts, html_class if html_class
  159. Array.wrap(optional_classes).each do |k, v|
  160. prepend_class opts, v if opts.delete k
  161. end
  162. end
  163. 1 include BasicTags
  164. 1 include Delegate
  165. end
  166. end

card-mod-bootstrap/lib/bootstrap/component/carousel.rb

27.5% lines covered

40 relevant lines. 11 lines covered and 29 lines missed.
    
  1. 1 class Bootstrap
  2. 1 class Component
  3. 1 class Carousel < Component
  4. 1 def render_content
  5. carousel *@args, &@build_block
  6. end
  7. 1 def carousel id, active_index, &block
  8. @id = id
  9. @active_item_index = active_index
  10. @items = []
  11. instance_exec &block
  12. @html.div class: "carousel slide", id: id, "data-ride" => "carousel" do
  13. indicators
  14. items
  15. control_prev
  16. control_next
  17. end
  18. end
  19. 1 def item content=nil, &block
  20. @items << (content || block)
  21. end
  22. 1 def items
  23. @html.div class: "carousel-inner", role: "listbox" do
  24. @items.each_with_index do |item, index|
  25. html_opts = { class: "carousel-item" }
  26. add_class html_opts, "active" if index == @active_item_index
  27. @html.div html_opts do
  28. item = item.call if item.respond_to?(:call)
  29. @html << item if item.is_a?(String)
  30. end
  31. end
  32. end
  33. end
  34. 1 def control_prev
  35. @html.a class: "carousel-control-prev", href: "##{@id}", role: "button",
  36. "data-slide" => "prev" do
  37. @html.span class: "carousel-control-prev-icon", "aria-hidden" => "true"
  38. @html.span "Previous", class: "sr-only"
  39. end
  40. end
  41. 1 def control_next
  42. @html.a class: "carousel-control-next", href: "##{@id}", role: "button",
  43. "data-slide": "next" do
  44. @html.span class: "carousel-control-next-icon", "aria-hidden" => "true"
  45. @html.span "Next", class: "sr-only"
  46. end
  47. end
  48. 1 def indicators
  49. @html.ol class: "carousel-indicators" do
  50. @items.size.times { |i| indicator i }
  51. end
  52. end
  53. 1 def indicator index
  54. html_opts = { "data-slide-to" => index, "data-target": "##{@id}" }
  55. add_class html_opts, "active" if index == @active_item_index
  56. @html.li html_opts
  57. end
  58. end
  59. end
  60. end

card-mod-bootstrap/lib/bootstrap/component/form.rb

41.67% lines covered

24 relevant lines. 10 lines covered and 14 lines missed.
    
  1. 1 class Bootstrap
  2. 1 class Component
  3. 1 class Form < Component
  4. 1 def render_content *args
  5. form *args, &@build_block
  6. end
  7. #
  8. # def_tag_method :form, nil, optional_classes: {
  9. # horizontal: "form-horizontal",
  10. # inline: "form-inline"
  11. # }
  12. # def_div_method :group, "form-group"
  13. # def_tag_method :label, nil
  14. # def_tag_method :input, "form-control" do |opts, extra_args|
  15. # type, label = extra_args
  16. # prepend { label label, for: opts[:id] } if label
  17. # opts[:type] = type
  18. # opts
  19. # end
  20. 1 def form opts={}, &block
  21. add_class opts, "form-horizontal" if opts.delete(:horizontal)
  22. add_class opts, "form-inline" if opts.delete(:inline)
  23. @html.form opts do
  24. instance_exec &block
  25. end
  26. end
  27. 1 def group text=nil, &block
  28. @html.div text, class: "form-group" do
  29. instance_exec &block
  30. end
  31. end
  32. 1 def label text=nil, &block
  33. @html.label text, &block
  34. end
  35. 1 def input type, text: nil, label: nil, id: nil
  36. @html.input id: id, class: "form-control", type: type do
  37. @html.label label, for: id if label
  38. @html << text if text
  39. end
  40. end
  41. 1 %i[text password datetime datetime-local date month time
  42. week number email url search tel color].each do |tag|
  43. # def_tag_method tag, "form-control", attributes: { type: tag },
  44. # tag: :input do |opts, extra_args|
  45. # label, = extra_args
  46. # prepend { label label, for: opts[:id] } if label
  47. # opts
  48. # end
  49. 14 define_method tag do |text: nil, id:, label: |
  50. @html.input id: id, class: "form-control", type: tag do
  51. @html.label label, for: id if label
  52. @html << text
  53. end
  54. end
  55. end
  56. end
  57. end
  58. end

card-mod-bootstrap/lib/bootstrap/component/horizontal_form.rb

44.83% lines covered

29 relevant lines. 13 lines covered and 16 lines missed.
    
  1. 1 class Bootstrap
  2. 1 class Component
  3. 1 class HorizontalForm < Form
  4. 1 def left_col_width
  5. @child_args.last && @child_args.last[0] || 2
  6. end
  7. 1 def right_col_width
  8. @child_args.last && @child_args.last[1] || 10
  9. end
  10. 1 def_tag_method :form, "form-horizontal"
  11. 1 def_tag_method :label, "control-label" do |opts, _extra_args|
  12. prepend_class opts, "col-sm-#{left_col_width}"
  13. opts
  14. end
  15. # def_div_method :input, nil do |opts, extra_args, &block|
  16. # type, label = extra_args
  17. # prepend { tag(:label, nil, for: opts[:id]) { label } } if label
  18. # insert { inner_input opts.merge(type: type) }
  19. # { class: "col-sm-#{right_col_width}" }
  20. # end
  21. 1 def label_col label, id:
  22. @html.label label, for: id, class: "col-sm-#{left_col_width} control-label"
  23. end
  24. 1 def input type, label:, id:, &block
  25. label_col label, id: id
  26. @html.div class: "col-sm-#{right_col_width}" do
  27. @html.input type: type, id: id, class: "form-control"
  28. end
  29. # block.call class: "col-sm-#{right_col_width}" do
  30. # inner_input opts.merge(type: type)
  31. # end
  32. end
  33. 1 def_tag_method :inner_input, "form-control", tag: :input
  34. 1 def_div_method :inner_checkbox, "checkbox"
  35. 1 def_div_method :checkbox, nil do |opts, extra_args|
  36. inner_checkbox do
  37. label do
  38. inner_input "checkbox", extra_args.first, opts
  39. end
  40. end
  41. { class: "col-sm-offset-#{left_col_width} col-sm-#{right_col_width}" }
  42. end
  43. 1 def checkbox text, extra_args
  44. @html.div class: "col-sm-offset-#{left_col_width} col-sm-#{right_col_width}" do
  45. @html.div class: "checkbox" do
  46. label_cllabel do
  47. inner_input "checkbox"
  48. end
  49. end
  50. end
  51. end
  52. end
  53. end
  54. end

card-mod-bootstrap/lib/bootstrap/component/layout.rb

85.0% lines covered

40 relevant lines. 34 lines covered and 6 lines missed.
    
  1. 1 class Bootstrap
  2. 1 class Component
  3. # generate bootstrap column layout
  4. # @example
  5. # layout container: true, fluid: true, class: "hidden" do
  6. # row 6, 6, class: "unicorn" do
  7. # column "horn",
  8. # column "rainbow", class: "colorful"
  9. # end
  10. # end
  11. # @example
  12. # layout do
  13. # row 3, 3, 4, 2, class: "unicorn" do
  14. # [ "horn", "body", "tail", "rainbow"]
  15. # end
  16. # add_html "<span> some extra html</span>"
  17. # row 6, 6, ["unicorn", "rainbow"], class: "horn"
  18. # end
  19. 1 class Layout < OldComponent
  20. 1 def render
  21. @rendered = begin
  22. 4 render_content
  23. 4 @content[-1]
  24. end
  25. end
  26. 1 def render_content
  27. 4 content = instance_exec *@args, &@build_block
  28. 4 add_content content
  29. 4 opts = @args.first
  30. 4 return unless opts && opts.delete(:container)
  31. 2 content = @content.pop
  32. 2 @content = ["".html_safe]
  33. 2 container content, opts
  34. end
  35. 1 add_div_method :container, nil do |opts, _extra_args|
  36. 2 prepend_class opts, opts.delete(:fluid) ? "container-fluid" : "container"
  37. 2 opts
  38. end
  39. # @param args column widths, column content and html attributes
  40. # @example
  41. # row 6, 6, ["col one", "col two"], class: "count", id: "count"
  42. # @example
  43. # row md: 12, xs: 8, "single column content"
  44. # @example
  45. # row md: [1, 11], xs: [2, 10] do
  46. # col "A"
  47. # col "B"
  48. # end
  49. 1 add_div_method :row, "row", content_processor: :column do |opts, extra_args|
  50. 4 cols_content = extra_args.pop if extra_args.last.is_a? Array
  51. 4 [opts, col_widths(extra_args, opts), cols_content].compact
  52. end
  53. # default column width type is for medium devices (col-md-)
  54. 1 add_div_method :column, nil do |opts, _extra_args|
  55. 4 @child_args.last.each do |medium, size|
  56. 4 if medium == :xs
  57. 4 prepend_class opts, "col-#{size.shift}"
  58. else
  59. prepend_class opts, "col-#{medium}-#{size.shift}"
  60. end
  61. end
  62. 4 opts
  63. end
  64. 1 alias_method :col, :column
  65. 1 private
  66. 1 def standardize_row_args args
  67. opts = args.last.is_a?(Hash) ? args.pop : {}
  68. cols = (args.last.is_a?(Array) || args.last.is_a?(String)) &&
  69. Array.wrap(args.pop)
  70. [cols, opts, col_widths(args, opts)]
  71. end
  72. 1 def col_widths args, opts
  73. 4 opts = args.pop if args.one? && args.last.is_a?(Hash)
  74. 4 if args.present?
  75. raise Error, "bad argument" unless args.all? { |a| a.is_a? Integer }
  76. { md: Array.wrap(args) }
  77. else
  78. 4 %i[lg xs sm md].each_with_object({}) do |k, cols_w|
  79. 16 next unless (widths = opts.delete(k))
  80. 2 cols_w[k] = Array.wrap widths
  81. end
  82. end
  83. end
  84. end
  85. end
  86. end

card-mod-bootstrap/lib/bootstrap/component/panel.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. 1 class Bootstrap
  2. 1 class Component
  3. 1 class Panel < OldComponent
  4. 1 def_div_method :panel, "card"
  5. 1 def_div_method :heading, "card-header"
  6. 1 def_div_method :body, "card-body"
  7. end
  8. end
  9. end

card-mod-bootstrap/lib/bootstrap/component_loader.rb

100.0% lines covered

16 relevant lines. 16 lines covered and 0 lines missed.
    
  1. 1 class Bootstrap
  2. 1 module ComponentLoader
  3. 1 def load_components
  4. 1 components.each do |component|
  5. 5 require_relative "component/#{component}"
  6. 5 include_component component
  7. end
  8. end
  9. 1 def include_component component
  10. 5 component_class = to_const component.camelcase
  11. 5 define_method component do |*args, &block|
  12. 6 component_class.render self, *args, &block
  13. end
  14. end
  15. 1 def components
  16. 2 path = File.expand_path "../component/*.rb", __FILE__
  17. 2 Dir.glob(path).map do |file|
  18. 10 File.basename file, ".rb"
  19. end
  20. end
  21. 1 def to_const name
  22. 5 self.class.const_get "::Bootstrap::Component::#{name.camelcase}"
  23. end
  24. end
  25. end

card-mod-bootstrap/lib/bootstrap/delegate.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. 1 class Bootstrap
  2. 1 module Delegate
  3. 1 def method_missing method_name, *args, &block
  4. # return super unless @context.respond_to? method_name
  5. 108 if block_given?
  6. @context.send(method_name, *args, &block)
  7. else
  8. 108 @context.send(method_name, *args)
  9. end
  10. end
  11. 1 def respond_to_missing? method_name, _include_private=false
  12. @context.respond_to? method_name
  13. end
  14. end
  15. end

card-mod-bootstrap/lib/bootstrap/old_component.rb

81.0% lines covered

100 relevant lines. 81 lines covered and 19 lines missed.
    
  1. #! no set module
  2. 1 class Bootstrap
  3. 1 class OldComponent < Component
  4. 1 def initialize context, *args, &block
  5. 6 @context = context
  6. 6 @content = ["".html_safe]
  7. 6 @args = args
  8. 6 @child_args = []
  9. 6 @append = []
  10. 6 @wrap = []
  11. 6 @build_block = block
  12. end
  13. 1 class << self
  14. 1 def render format, *args, &block
  15. 6 new(format, *args, &block).render
  16. end
  17. # Like add_tag_method but always generates a div tag
  18. # The tag option is not available
  19. 1 def add_div_method name, html_class, opts={}, &tag_block
  20. 8 add_tag_method name, html_class, opts.merge(tag: :div), &tag_block
  21. end
  22. # Defines a method that generates a html tag
  23. # @param name [Symbol, String] the name of the method. If no :tag option in tag_opts is defined then the name is also the name of the tag that the method generates
  24. # @param html_class [String] a html class that is added to tag. Use nil if you don't want a html_class
  25. # @param tag_opts [Hash] additional argument that will be added to the tag
  26. # @option tag_opts [Symbol, String] tag the name of the tag
  27. # @example
  28. # add_tag_method :link, "known-link", tag: :a, id: "uniq-link"
  29. # link # => <a class="known-link" id="uniq-link"></a>
  30. 1 def add_tag_method name, html_class, tag_opts={}, &tag_block
  31. 9 define_method name do |*args, &block|
  32. 20 process_tag tag_opts[:tag] || name do
  33. 20 content, opts, new_child_args = standardize_args args, &tag_block
  34. 20 add_classes opts, html_class, tag_opts.delete(:optional_classes)
  35. 20 if (attributes = tag_opts.delete(:attributes))
  36. opts.merge! attributes
  37. end
  38. 20 content = with_child_args new_child_args do
  39. 20 generate_content content,
  40. tag_opts[:content_processor],
  41. &block
  42. end
  43. 20 [content, opts]
  44. end
  45. end
  46. end
  47. 1 alias_method :def_div_method, :add_div_method
  48. 1 alias_method :def_tag_method, :add_tag_method
  49. end
  50. 1 def render
  51. @rendered = begin
  52. 2 render_content
  53. 2 @content[-1]
  54. end
  55. end
  56. 1 def prepend &block
  57. tmp = @content.pop
  58. instance_exec &block
  59. @content << tmp
  60. end
  61. 1 def insert &block
  62. instance_exec &block
  63. end
  64. 1 def append &block
  65. @append[-1] << block
  66. end
  67. 1 def wrap tag=nil, &block
  68. @wrap[-1] << (block_given? ? block : tag)
  69. end
  70. 1 private
  71. 1 def render_content
  72. # if @build_block.arity > 0
  73. 2 instance_exec *@args, &@build_block
  74. end
  75. 1 def generate_content content, processor, &block
  76. 20 content = instance_exec &block if block.present?
  77. 20 return content if !processor || !content.is_a?(Array)
  78. content.each {|item| send processor, item}
  79. ""
  80. end
  81. 1 def with_child_args args
  82. 20 @child_args << args if args.present?
  83. 20 res = yield
  84. 20 @child_args.pop if args.present?
  85. 20 res
  86. end
  87. 1 def add_content content
  88. 48 @content[-1] << "\n#{content}".html_safe if content.present?
  89. end
  90. 1 def process_tag tag_name, &content_block
  91. 20 @content.push "".html_safe
  92. 20 @append << []
  93. 20 @wrap << []
  94. 20 content, opts = content_block.call
  95. 20 wrappers = @wrap.pop
  96. 20 if wrappers.present?
  97. while wrappers.present? do
  98. wrapper = wrappers.shift
  99. if wrapper.is_a? Symbol
  100. send wrapper, &content_block
  101. else
  102. instance_exec(content, &wrappers.shift)
  103. end
  104. end
  105. else
  106. 20 add_content content
  107. end
  108. 20 collected_content = @content.pop
  109. 20 tag_name = opts.delete(:tag) if tag_name == :yield
  110. 20 add_content content_tag(tag_name, collected_content, opts, false)
  111. 20 @append.pop.each do |block|
  112. add_content instance_exec(&block)
  113. end
  114. 20 ""
  115. end
  116. 1 def standardize_args args, &block
  117. 20 opts = args.last.is_a?(Hash) ? args.pop : {}
  118. 20 items = ((args.one? && args.last.is_a?(String)) || args.last.is_a?(Array)) &&
  119. args.pop
  120. 20 if block.present?
  121. 16 opts, args = instance_exec opts, args, &block
  122. 16 unless opts.is_a?(Hash)
  123. raise Card::Error, "first return value of a tag block has to be a hash"
  124. end
  125. end
  126. 20 [items, opts, args]
  127. end
  128. 1 def add_classes opts, html_class, optional_classes
  129. 20 prepend_class opts, html_class if html_class
  130. 20 Array.wrap(optional_classes).each do |k, v|
  131. prepend_class opts, v if opts.delete k
  132. end
  133. end
  134. # include BasicTags
  135. 1 def html content
  136. 4 add_content String(content).html_safe
  137. 4 ""
  138. end
  139. 1 add_div_method :div, nil do |opts, extra_args|
  140. 4 prepend_class opts, extra_args.first if extra_args.present?
  141. 4 opts
  142. end
  143. 1 add_div_method :span, nil do |opts, extra_args|
  144. prepend_class opts, extra_args.first if extra_args.present?
  145. opts
  146. end
  147. 1 add_tag_method :tag, nil, tag: :yield do |opts, extra_args|
  148. 2 prepend_class opts, extra_args[1] if extra_args[1].present?
  149. 2 opts[:tag] = extra_args[0]
  150. 2 opts
  151. end
  152. 1 include Delegate
  153. end
  154. end

card-mod-bootstrap/lib/bootstrapper.rb

88.89% lines covered

9 relevant lines. 8 lines covered and 1 lines missed.
    
  1. 1 require "bootstrap"
  2. 1 module Bootstrapper
  3. 1 extend Bootstrap::ComponentLoader
  4. 1 def bootstrap
  5. 6 @bootstrap ||= ::Bootstrap.new(self)
  6. end
  7. 1 def bs *args, &block
  8. bootstrap.render *args, &block
  9. end
  10. 1 components.each do |component|
  11. 5 delegate component, to: :bootstrap, prefix: :bs
  12. end
  13. end

card-mod-bootstrap/lib/card/lazy_tab.rb

95.0% lines covered

20 relevant lines. 19 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 class LazyTab < Tab
  3. 1 def url
  4. 8 @url ||= (config_hash? && @config[:path]) || format.path(view: view)
  5. end
  6. 1 def view
  7. 4 @view ||= (config_hash? && @config[:view]) || @config
  8. end
  9. 1 def tab_button
  10. 4 if url
  11. 4 super
  12. else
  13. wrap_with(:li, label, role: "presentation")
  14. end
  15. end
  16. 1 def button_attrib
  17. 12 @button_attrib ||= super.merge("data-url" => url.html_safe)
  18. end
  19. 1 def tab_button_link
  20. 4 add_class button_attrib, "load" unless active?
  21. 4 super
  22. end
  23. 1 def content
  24. 4 @content ||= ""
  25. end
  26. 1 def tab_pane args=nil, &block
  27. 4 @content = yield if active? && block_given?
  28. 4 super
  29. end
  30. end
  31. end

card-mod-bootstrap/lib/card/tab.rb

97.5% lines covered

40 relevant lines. 39 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 class Tab
  3. 1 attr_reader :format, :name, :label, :content, :button_attrib
  4. 1 class << self
  5. 1 def tab_objects format, tab_hash, active_name, klass=nil
  6. 2 klass ||= Card::Tab
  7. 2 active_name ||= tab_hash.keys.first
  8. 2 tab_hash.map do |name, config|
  9. 4 klass.new format, name, active_name, config
  10. end
  11. end
  12. end
  13. 1 delegate :add_class, :wrap_with, :unique_id, :link_to, to: :format
  14. 1 def initialize format, name, active_name, config
  15. 4 @format = format
  16. 4 @name = name
  17. 4 @active_name = active_name
  18. 4 @config = config
  19. end
  20. 1 def tab_button
  21. 4 add_class button_attrib, "active" if active?
  22. 4 wrap_with :li, tab_button_link,
  23. role: :presentation,
  24. class: "nav-item tab-li-#{name}"
  25. end
  26. 1 def tab_pane args=nil
  27. 4 pane_attr = { role: :tabpanel, id: tab_id }
  28. 4 pane_attr.merge! args if args.present?
  29. 4 add_class pane_attr, "tab-pane tab-pane-#{name}"
  30. 4 add_class pane_attr, "active" if active?
  31. 4 wrap_with :div, content, pane_attr
  32. end
  33. 1 private
  34. 1 def config_hash?
  35. 16 @config.is_a? Hash
  36. end
  37. 1 def label
  38. 4 @label ||= (config_hash? && @config[:title]) || name
  39. end
  40. 1 def content
  41. @content ||= config_hash? ? @config[:content] : @config
  42. end
  43. 1 def button_attrib
  44. 4 @button_attrib ||= (config_hash? && @config[:button_attr]) || {}
  45. end
  46. 1 def tab_button_link
  47. 4 add_class button_attrib, "nav-link"
  48. 4 link_to label, button_attrib.merge(
  49. path: "##{tab_id}",
  50. role: "tab",
  51. "data-toggle" => "tab",
  52. "data-tab-name" => name
  53. )
  54. end
  55. 1 def tab_id
  56. 8 @tab_id ||= "#{unique_id}-#{name.to_name.safe_key}"
  57. end
  58. 1 def active?
  59. 16 name == @active_name
  60. end
  61. end
  62. end

card-mod-carrierwave/lib/carrier_wave/card_mount.rb

100.0% lines covered

12 relevant lines. 12 lines covered and 0 lines missed.
    
  1. 1 require "carrierwave"
  2. 1 module CarrierWave
  3. # adapt carrierwave mount to cards
  4. 1 module CardMount
  5. 1 include CarrierWave::Mount
  6. 1 def uploaders
  7. 2 Card.uploaders ||= {}
  8. end
  9. 1 def uploader_options
  10. 2 Card.uploader_options ||= {}
  11. end
  12. 1 def mount_uploader column, uploader=nil, options={}, &block
  13. 2 options[:mount_on] ||= :db_content
  14. 2 super
  15. 2 class_eval <<-RUBY, __FILE__, __LINE__ + 1
  16. event :store_#{column}_event, :finalize,
  17. on: :save, when: :store_#{column}_event? do
  18. store_#{column}!
  19. end
  20. # remove files only if card has no history
  21. event :remove_#{column}_event, :finalize,
  22. on: :delete, when: proc { |c| !c.history? } do
  23. remove_#{column}!
  24. end
  25. event :mark_remove_#{column}_false_event, :finalize,
  26. on: :update do
  27. mark_remove_#{column}_false
  28. end
  29. event :store_previous_changes_for_#{column}_event, :store,
  30. on: :update, when: proc { |c| !c.history? } do
  31. store_previous_changes_for_#{column}
  32. end
  33. event :remove_previously_stored_#{column}_event, :finalize,
  34. on: :update, when: proc { |c| !c.history?} do
  35. remove_previously_stored_#{column}
  36. end
  37. # don't attempt to store coded images unless ENV specifies it
  38. def store_#{column}_event?
  39. !coded? || ENV["STORE_CODED_FILES"]
  40. end
  41. def attachment
  42. #{column}
  43. end
  44. def store_attachment!
  45. store_#{column}!
  46. end
  47. def attachment_name
  48. "#{column}".to_sym
  49. end
  50. def read_uploader *args
  51. read_attribute *args
  52. end
  53. def write_uploader *args
  54. write_attribute *args
  55. end
  56. def #{column}=(new_file)
  57. return if new_file.blank?
  58. assign_file(new_file) { super }
  59. end
  60. def remote_#{column}_url=(url)
  61. assign_file(url) { super }
  62. end
  63. def assign_file file
  64. db_column = _mounter(:#{column}).serialization_column
  65. send(:"\#{db_column}_will_change!") # unless attribute_is_changing? db_column
  66. if web?
  67. self.content = file
  68. else
  69. send(:"#{column}_will_change!")
  70. yield
  71. end
  72. end
  73. def remove_#{column}=(value)
  74. column = _mounter(:#{column}).serialization_column
  75. send(:"\#{column}_will_change!")
  76. super
  77. end
  78. def remove_#{column}!
  79. self.remove_#{column} = true
  80. write_#{column}_identifier
  81. self.remove_#{column} = false
  82. super
  83. end
  84. def #{column}_will_change!
  85. @#{column}_changed = true
  86. @#{column}_is_changing = true
  87. end
  88. def #{column}_is_changing?
  89. @#{column}_is_changing
  90. end
  91. def #{column}_changed?
  92. @#{column}_changed
  93. end
  94. def serializable_hash(options=nil)
  95. hash = {}
  96. except = options && options[:except] &&
  97. Array.wrap(options[:except]).map(&:to_s)
  98. only = options && options[:only] &&
  99. Array.wrap(options[:only]).map(&:to_s)
  100. self.class.uploaders.each do |column, uploader|
  101. if (!only && !except) || (only && only.include?(column.to_s)) ||
  102. (!only && except && !except.include?(column.to_s))
  103. hash[column.to_s] = _mounter(column).uploader.serializable_hash
  104. end
  105. end
  106. super(options).merge(hash)
  107. end
  108. RUBY
  109. end
  110. end
  111. end

card-mod-carrierwave/lib/carrier_wave/file_card_uploader.rb

89.77% lines covered

88 relevant lines. 79 lines covered and 9 lines missed.
    
  1. 1 module CarrierWave
  2. 1 class << self
  3. 1 def tmp_path
  4. 220 @tmp_path ||= Card.paths["tmp"].existent.first
  5. end
  6. end
  7. 1 class SanitizedFile
  8. 1 def content_type
  9. # the original content_type method doesn't seem to be very reliable
  10. # It uses mime_magic_content_type - which returns invalid/invalid for css files
  11. # that start with a comment - as the second option. (we switch the order and
  12. # use it as the third option)
  13. 727 @content_type ||=
  14. existing_content_type ||
  15. mini_mime_content_type ||
  16. mime_magic_content_type
  17. end
  18. end
  19. 1 module Uploader
  20. # Implements a different name pattern for versions than CarrierWave's
  21. # default: we expect the version name at the end of the filename separated
  22. # by a dash
  23. 1 module Versions
  24. 1 private
  25. # put version at the end of the filename
  26. 1 def full_filename for_file
  27. 3867 name = super(for_file)
  28. 3867 parts = name.split "."
  29. 3867 basename = [parts.shift, version_name].compact.join("-")
  30. 3867 "#{basename}.#{parts.join('.')}"
  31. end
  32. end
  33. end
  34. # Takes care of the file upload for cards with attached files.
  35. # Most of the upload behaviour depends on the card itself.
  36. # (e.g. card type and storage option chosen for the card). So in contrary
  37. # to CarrierWave's default uploader we depend very much on the model
  38. # (= card object) to get the correct paths for retrieving and storing
  39. # the file.
  40. #
  41. # Cards that support attachments (by default those are cards of type "file"
  42. # and "image") accept a file handle as a card attribute.
  43. #
  44. # @example Attaching a file to a file card
  45. # Card.create name: "file card", type: :file,
  46. # file: File.new(path_to_file)
  47. #
  48. # @example Attaching a image to a image card
  49. # Card.create name: "file card", type: :image,
  50. # image: File.new(path_to_image)
  51. #
  52. # It's possible to upload files using a url. The card attribute for that is
  53. # remote_<attachment_type>_url
  54. #
  55. # @example Create a file card using a remote url
  56. # Card.create name: "file_card", type: :file,
  57. # remote_file_url: "http://a.file.in/the.web"
  58. #
  59. # @example Updating a image card using a remote url
  60. # card.update remote_image_url: "http://a.image/somewhere.png"
  61. #
  62. # ## Storage types
  63. # You can choose between four different storage options
  64. # - coded: These files are in the codebase, like the default logo.
  65. # Every view is a decko request.
  66. # - local: Uploaded files which are stored in a local upload directory
  67. # (upload path is configurable via config.paths["files"]).
  68. # If read permissions are set such that "Anyone" can read, then there is
  69. # a symlink from the public directory. Otherwise every view is a decko
  70. # request.
  71. # - cloud: You can configure buckets that refer to an external storage
  72. # service. Link is rendered as absolute url
  73. # - web: A fixed url (to external source). No upload or other file
  74. # processing. Link is just the saved url.
  75. #
  76. # Currently, there is no web interface that let's a user or administrator
  77. # choose a storage option for a specific card or set of cards.
  78. # There is only a global config option to set the storage type for all new
  79. # uploads (config.storage_type). On the *admin card it's possible to
  80. # update all existing file cards according to the current global config.
  81. #
  82. # Storage types for single cards can be changed by developers using
  83. # the card attributes "storage_type", "bucket", and "mod".
  84. #
  85. # @example Creating a hard-coded file
  86. # Card.create name: "file card", type_id: Card::FileID,
  87. # file: File.new(path),
  88. # storage_type: :coded, mod: "account"
  89. #
  90. # @example Moving a file to a cloud service
  91. # # my_deck/config/application.rb:
  92. # config.file_buckets = {
  93. # aws_bucket: {
  94. # provider: "fog/aws",
  95. # directory: "bucket-name",
  96. # subdirectory: "files",
  97. # credentials: {
  98. # provider: 'AWS' # required
  99. # aws_access_key_id: 'key' # required
  100. # aws_secret_access_key: 'secret-key' # required
  101. # public: true,
  102. # }
  103. # }
  104. #
  105. # # decko console or rake task:
  106. # card.update storage_type: :cloud, bucket: :aws_bucket
  107. #
  108. # @example Creating a file card with fixed external link
  109. # Card.create name: "file card", type_id: Card::FileID,
  110. # content: "http://animals.org/cat.png"
  111. # storage_type: :web
  112. #
  113. # Card.create name: "file card", type_id: Card::FileID,
  114. # file: "http://animals.org/cat.png"
  115. # storage_type: :web
  116. #
  117. # Depending on the storage type the uploader uses the following paths
  118. # and identifiers.
  119. # ### Identifier (stored in the database as db_content)
  120. # - coded: :codename/mod_name.ext
  121. # - local: ~card_id/action_id.ext
  122. # - cloud: (bucket)/card_id/action_id.ext
  123. # - web: http://url
  124. #
  125. # ### Storage path
  126. # - coded:
  127. # mod_dir/file/codename/type_code(-variant).ext (no colon on codename!)
  128. # - local:
  129. # files_dir/card_id/action_id(-variant).ext (no tilde on id!)
  130. # - cloud:
  131. # bucket/bucket_subdir/id/action_id(-variant).ext
  132. # - web: no storage
  133. #
  134. # Variants are only used for images. Possible options are
  135. # icon|small|medium|large|original.
  136. # files_dir, bucket, and bucket_subdir can be changed via config options.
  137. #
  138. # ### Supported url patterns
  139. # mark.ext
  140. # mark/revision.ext
  141. # mark/revision-variant.ext
  142. # /files/mark/revision-variant.ext # <- public symlink if readable by
  143. # # "Anyone"
  144. #
  145. # <mark> can be one of the following options
  146. # - <card name>
  147. # - ~<card id>
  148. # - :<code name>
  149. #
  150. # <revision> is the mod name if the file is coded or and action_id in any
  151. # case
  152. #
  153. # Examples:
  154. # *logo.png
  155. # ~22/33-medium.png # local
  156. # :yeti_skin/standard-large.png # coded
  157. #
  158. 1 class FileCardUploader < Uploader::Base
  159. 1 attr_accessor :mod
  160. 1 include Card::Env::Location
  161. 1 STORAGE_TYPES = [:cloud, :web, :coded, :local].freeze
  162. 1 CONFIG_OPTIONS = [:provider, :attributes, :directory, :public, :credentials,
  163. :authenticated_url_expiration, :use_ssl_for_aws].freeze
  164. 1 CONFIG_CREDENTIAL_OPTIONS = [
  165. :provider,
  166. :aws_access_key_id, :aws_secret_access_key, :region, :host, :endpoint,
  167. :google_access_key_id, :google_secret_access_key
  168. ].freeze
  169. 1 delegate :store_dir, :retrieve_dir, :file_dir, :mod, :bucket, to: :model
  170. 1 def valid?
  171. 262 extension.present?
  172. end
  173. 1 def filename
  174. 2557 if model.coded?
  175. 2154 "#{model.type_code}#{extension}"
  176. else
  177. 403 "#{action_id}#{extension}"
  178. end
  179. end
  180. 1 def extension
  181. case
  182. 6397 when file&.extension.present? then ".#{file.extension}"
  183. 2335 when card_content = model.content then File.extname(card_content)
  184. when orig = original_filename then File.extname(orig)
  185. else ""
  186. end.downcase
  187. end
  188. # generate identifier that gets stored in the card's db_content field
  189. # @param opts [Hash] generate an identifier using the given storage options
  190. # instead of the storage options derived from the model and
  191. # the global configuration
  192. # @option opts [Symbol] storage_type
  193. # @option opts [String] mod
  194. # @option opts [Symbol] bucket
  195. 1 def db_content opts={}
  196. 31 model.with_storage_options opts do
  197. 31 return model.content if model.web?
  198. 31 return "" unless file.present?
  199. 31 "%s/%s" % [file_dir, url_filename]
  200. end
  201. end
  202. 1 def url_filename opts={}
  203. 1226 model.with_storage_options opts do
  204. 1226 if model.coded?
  205. 1136 "#{model.mod}#{extension}"
  206. else
  207. 90 "#{action_id}#{extension}"
  208. end
  209. end
  210. end
  211. # @option opts [Symbol] :absolute - return absolute url
  212. 1 def url opts={}
  213. 1195 if model.cloud?
  214. file&.url
  215. 1195 elsif model.web?
  216. model.content
  217. else
  218. 1195 local_url opts
  219. end
  220. end
  221. 1 def local_url opts={}
  222. 1195 "%s/%s/%s" % [local_url_base(opts), file_dir, full_filename(url_filename(opts))]
  223. end
  224. 1 def local_url_base opts={}
  225. 1195 web_path = Card.config.files_web_path
  226. 1195 opts.delete(:absolute) ? card_url(web_path) : card_path(web_path)
  227. end
  228. 1 def public_path
  229. 37 File.join Cardio.paths["public"].existent.first, url
  230. end
  231. 1 def cache_dir
  232. 183 @model.files_base_dir + "/cache"
  233. end
  234. # Carrierwave calls store_path without argument when it stores the file
  235. # and with the identifier from the db when it retrieves the file.
  236. # In our case the first part of our identifier is not part of the path
  237. # but we can construct the filename from db data. So we don't need the
  238. # identifier.
  239. 1 def store_path for_file=nil
  240. 2524 if for_file
  241. 2451 retrieve_path
  242. else
  243. 73 File.join([store_dir, full_filename(filename)].compact)
  244. end
  245. end
  246. 1 def retrieve_path
  247. 2451 File.join([retrieve_dir, full_filename(filename)].compact)
  248. end
  249. 1 def tmp_path
  250. Dir.mkdir model.tmp_upload_dir unless Dir.exist? model.tmp_upload_dir
  251. File.join model.tmp_upload_dir, filename
  252. end
  253. 1 def create_versions? new_file
  254. 2356 model.create_versions? new_file
  255. end
  256. # paperclip compatibility used in type/file.rb#core (base format)
  257. 1 def path version=nil
  258. 51 version ? versions[version].path : super()
  259. end
  260. 1 def original_filename
  261. 238 @original_filename ||= model.selected_action &&
  262. model.selected_action.comment
  263. end
  264. 1 def action_id
  265. 493 model.selected_content_action_id || action_id_stand_in
  266. end
  267. # delegate carrierwave's fog config methods to bucket configuration
  268. 1 ::CarrierWave::FileCardUploader::CONFIG_OPTIONS.each do |name|
  269. 7 define_method("fog_#{name}") { bucket_config name }
  270. end
  271. 1 def bucket_config option
  272. @model.bucket_config[option]
  273. end
  274. 1 def asset_host
  275. bucket_config(:asset_host) || super
  276. end
  277. 1 private
  278. # used as action_id in the filename
  279. # if card is not #actionable?
  280. 1 def action_id_stand_in
  281. 1 Time.now.to_i
  282. end
  283. 1 def storage
  284. 2546 case @model.storage_type
  285. when :cloud
  286. ::CarrierWave::Storage::Fog.new(self)
  287. else
  288. 2546 ::CarrierWave::Storage::File.new(self)
  289. end
  290. end
  291. end
  292. end

card-mod-carrierwave/lib/carrier_wave/image_card_uploader.rb

100.0% lines covered

22 relevant lines. 22 lines covered and 0 lines missed.
    
  1. 1 require "mini_magick"
  2. 1 module CarrierWave
  3. # Adds image specific version handling to {FileCardUploader}.
  4. # The ImageCardUploader creates five versions of different sizes when it
  5. # uploads an imagae file:
  6. # icon (16x16), small (75x75), medium (200X200), large (500x500) and
  7. # the original size.
  8. 1 class ImageCardUploader < FileCardUploader
  9. 1 include CarrierWave::MiniMagick
  10. 1 def path version=nil
  11. 48 (version && version != :original) ? versions[version].path : super()
  12. end
  13. 1 version :icon, if: :create_versions?, from_version: :small do
  14. 1 process resize_and_pad: [16, 16]
  15. end
  16. 1 version :small, if: :create_versions?, from_version: :medium do
  17. 1 process resize_to_fit: [75, 75]
  18. end
  19. 1 version :medium, if: :create_versions? do
  20. 1 process resize_to_limit: [200, 200]
  21. end
  22. 1 version :large, if: :create_versions? do
  23. 1 process resize_to_limit: [500, 500]
  24. end
  25. # version :small_square, if: :create_versions?,
  26. # from_version: :medium_square do
  27. # process resize_to_fill: [75, 75]
  28. # end
  29. # version :medium_square, if: :create_versions? do
  30. # process resize_to_fill: [200, 200]
  31. # end
  32. #
  33. # In case we decide to support the squared versions
  34. # we have to update all existing images with the following snippet:
  35. # Card.search(type_id: Card::ImageID) do |card|
  36. # card.image.cache_stored_file!
  37. # card.image.recreate_versions!
  38. # end
  39. 1 def identifier
  40. 38 full_filename(super())
  41. end
  42. # add 'original' if no version is given
  43. 1 def full_filename for_file
  44. 2279 name = super(for_file)
  45. 2279 if version_name
  46. 1669 name
  47. else
  48. 610 parts = name.split "."
  49. 610 "#{parts.shift}-original.#{parts.join('.')}"
  50. end
  51. end
  52. end
  53. end

card-mod-delayed_job/lib/card/mod/delayed_job.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 require "delayed_job_active_record"

card-mod-delayed_job/lib/cardio/delayed_job.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. 1 module Cardio
  2. # override default methods to handle DelayedJob needs
  3. 1 module DelayedJob
  4. 1 def delaying! on=true
  5. 18 super
  6. 18 Delayed::Worker.delay_jobs = Cardio.config.delaying
  7. end
  8. end
  9. end

card-mod-email/format/email_html_format.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. # Format text for use in html email messages
  5. 1 class EmailHtmlFormat < Card::Format::HtmlFormat
  6. 1 @@aliases["email"] = "email_html"
  7. 1 def self.view_caching?
  8. false
  9. end
  10. end
  11. end
  12. end

card-mod-email/format/email_text_format.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. # Format text for use in plain text email messages
  5. 1 class EmailTextFormat < Card::Format::TextFormat
  6. 1 def chunk_list
  7. 48 :references
  8. end
  9. end
  10. end
  11. end

card-mod-follow/lib/card/follow_option.rb

80.0% lines covered

15 relevant lines. 12 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # module to be included in cards used as options for follow rules
  4. 1 module FollowOption
  5. # Hash containing an applicability test for each option (block)
  6. 1 @test = {}
  7. # Hash containing an id-list-generating block for each option
  8. 1 @follower_candidate_ids = {}
  9. # Hash that registers / groups options
  10. 1 @options = { all: [], main: [], restrictive: [] }
  11. 1 class << self
  12. 1 attr_reader :test, :follower_candidate_ids, :options
  13. 1 def codenames type=:all
  14. 8 options[type]
  15. end
  16. 1 def cards
  17. codenames.map { |codename| Card[codename] }
  18. end
  19. 1 def restrictive_options
  20. codenames :restrictive
  21. end
  22. 1 def main_options
  23. codenames :main
  24. end
  25. end
  26. end
  27. end

card-mod-follow/lib/card/follower_stash.rb

97.83% lines covered

46 relevant lines. 45 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. # stash followers of a given card
  3. 1 class FollowerStash
  4. 1 def initialize card=nil
  5. 104 @stash = Hash.new { |h, v| h[v] = [] }
  6. 86 @checked = ::Set.new
  7. 86 check_card(card) if card
  8. end
  9. 1 def check_card card
  10. 166 return if @checked.include? card.key
  11. 165 Auth.as_bot do
  12. 165 @checked.add card.key
  13. 165 stash_direct_followers card
  14. 165 stash_field_followers card.left
  15. end
  16. end
  17. 1 def followers
  18. @stash.keys
  19. end
  20. 1 def each_follower_with_reason
  21. # "follower"(=user) is a card object, "followed"(=reasons) a card name
  22. 86 @stash.each do |follower_card, reasons|
  23. 18 yield(follower_card, reasons.first)
  24. end
  25. end
  26. 1 private
  27. 1 def stash_direct_followers card
  28. 165 card.each_direct_follower_id_with_reason do |user_id, reason|
  29. 20 stash Card.fetch(user_id), reason
  30. end
  31. end
  32. 1 def stash_field_followers card
  33. 165 return unless (fields = follow_fields card)
  34. 88 fields.each do |field|
  35. 88 break if stash_field_follower card, field
  36. end
  37. end
  38. 1 def stash_field_follower card, field
  39. 88 return false unless checked?(field.to_name) || nested?(card, field)
  40. 11 check_card card
  41. 11 true
  42. end
  43. 1 def nested? card, field
  44. 88 return unless field.to_name.key == includes_card_key
  45. 88 @checked.intersection(nestee_set(card)).any?
  46. end
  47. 1 def includes_card_key
  48. 88 @includes_card_key ||= :nests.cardname.key
  49. end
  50. 1 def nestee_set card
  51. 88 @nestee_set ||= {}
  52. 88 @nestee_set[card.key] ||= nestee_search card
  53. end
  54. 1 def nestee_search card
  55. 88 Card.search({ return: "key", included_by: card.name },
  56. "follow cards included by #{card.name}")
  57. end
  58. 1 def checked? name
  59. 201 @checked.include? name.key
  60. end
  61. 1 def follow_fields card
  62. 165 return unless card && !checked?(card.name)
  63. 88 card.rule_card(:follow_fields)&.item_names(context: card.name)
  64. end
  65. 1 def stash follower, reason
  66. 20 @stash[follower] << reason
  67. end
  68. end
  69. end

card-mod-format/format/css_format.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. 1 class CssFormat < Format
  5. 1 register :css
  6. 1 def mime_type
  7. "text/css"
  8. end
  9. 1 def self.view_caching?
  10. false
  11. end
  12. end
  13. end
  14. end

card-mod-format/format/csv_format.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. 1 class CsvFormat < TextFormat
  5. 1 register :csv
  6. 1 def mime_type
  7. "text/comma-separated-values"
  8. end
  9. 1 def self.view_caching?
  10. # TODO: make view caching handle non-strings
  11. # (specifically stub_render)
  12. false
  13. end
  14. end
  15. end
  16. end

card-mod-format/format/file_format.rb

100.0% lines covered

3 relevant lines. 3 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. 1 class FileFormat < Format
  5. end
  6. end
  7. end

card-mod-format/format/js_format.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. 1 class JsFormat < Format
  5. 1 register :js
  6. 1 def self.view_caching?
  7. false
  8. end
  9. 1 def mime_type
  10. "text/javascript"
  11. end
  12. end
  13. end
  14. end

card-mod-format/format/json_format.rb

63.64% lines covered

11 relevant lines. 7 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. 1 class JsonFormat < DataFormat
  5. 1 register :json
  6. 1 def mime_type
  7. 11 "text/json"
  8. end
  9. 1 def expand_stubs content
  10. case content
  11. when Hash
  12. content.each { |k, v| content[k] = expand_stubs v }
  13. when Array
  14. content.map { |item| expand_stubs item }
  15. else
  16. super
  17. end
  18. end
  19. end
  20. end
  21. end

card-mod-format/format/rss_format.rb

100.0% lines covered

4 relevant lines. 4 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. 1 class RssFormat < HtmlFormat
  5. 1 register :rss
  6. end
  7. end
  8. end

card-mod-format/format/xml_format.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. 1 class XmlFormat < DataFormat
  5. 1 register :xml
  6. 1 def mime_type
  7. "text/xml"
  8. end
  9. end
  10. end
  11. end

card-mod-history/lib/card/act.rb

75.0% lines covered

44 relevant lines. 33 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # An "act" is a group of recorded {Card::Action actions} on {Card cards}.
  4. # Together, {Act acts}, {Action actions}, and {Change changes} comprise a
  5. # comprehensive {Card card} history tracking system.
  6. #
  7. # For example, if a given web form submissions updates the contents of three cards,
  8. # then the submission will result in the recording of three {Action actions}, each
  9. # of which is tied to one {Act act}.
  10. #
  11. # Each act records:
  12. #
  13. # - the _actor_id_ (an id associated with the account responsible)
  14. # - the _card_id_ of the act's primary card
  15. # - _acted_at_, a timestamp of the action
  16. # - the _ip_address_ of the actor where applicable.
  17. #
  18. 1 class Act < ApplicationRecord
  19. 1 before_save :assign_actor
  20. 307 has_many :ar_actions, -> { order :id }, foreign_key: :card_act_id,
  21. inverse_of: :act,
  22. class_name: "Card::Action"
  23. 1 class << self
  24. # remove all acts that have no card. (janitorial)
  25. #
  26. # CAREFUL - could still have actions even if act card is gone...
  27. 1 def delete_cardless
  28. left_join = "LEFT JOIN cards ON card_acts.card_id = cards.id"
  29. joins(left_join).where("cards.id IS NULL").delete_all
  30. end
  31. # remove all acts that have no action. (janitorial)
  32. 1 def delete_actionless
  33. joins(
  34. "LEFT JOIN card_actions ON card_acts.id = card_act_id"
  35. ).where(
  36. "card_actions.id is null"
  37. ).delete_all
  38. end
  39. # all acts with actions on a given list of cards
  40. # @param card_ids [Array of Integers]
  41. # @param with_drafts [true, false] (only shows drafts of current user)
  42. # @return [Array of Acts]
  43. 1 def all_with_actions_on card_ids, with_drafts=false
  44. sql = "card_actions.card_id IN (:card_ids) AND (draft is not true"
  45. sql << (with_drafts ? " OR actor_id = :user_id)" : ")")
  46. all_viewable([sql, { card_ids: card_ids, user_id: Card::Auth.current_id }])
  47. end
  48. # all acts with actions that current user has permission to view
  49. # @return [ActiveRecord Relation]
  50. 1 def all_viewable action_where=nil
  51. relation = joins(ar_actions: :ar_card)
  52. relation = relation.where(action_where) if action_where
  53. relation.where(Query::CardQuery.viewable_sql).where.not(card_id: nil).distinct
  54. end
  55. 1 def cache
  56. 115 Card::Cache[Card::Act]
  57. end
  58. # used by rails time_ago
  59. # timestamp is set by rails on create
  60. 1 def timestamp_attributes_for_create
  61. 1 super << "acted_at"
  62. end
  63. end
  64. 1 def actor
  65. 22 Card.fetch actor_id
  66. end
  67. # the act's primary card
  68. # @return [Card]
  69. 1 def card
  70. 36 Card.fetch card_id, look_in_trash: true # , skip_modules: true
  71. # FIXME: if the following is necessary, we need to document why.
  72. # generally it's a very bad idea to have type-specific code here.
  73. # return res unless res&.type_id&.in?([Card::FileID, Card::ImageID])
  74. # res.include_set_modules
  75. end
  76. # list of all actions that are part of the act
  77. # @return [Array]
  78. 1 def actions cached=true
  79. 201 return ar_actions unless cached
  80. 128 self.class.cache.fetch("#{id}-actions") { ar_actions.find_all.to_a }
  81. end
  82. # act's action on the card in question
  83. # @param card_id [Integer]
  84. # @return [Card::Action]
  85. 1 def action_on card_id
  86. 53 actions.find do |action|
  87. 59 action.card_id == card_id && !action.draft
  88. end
  89. end
  90. # act's action on primary card if it exists. otherwise act's first action
  91. # @return [Card::Action]
  92. 1 def main_action
  93. 53 action_on(card_id) || actions.first
  94. end
  95. 1 def draft?
  96. main_action.draft
  97. end
  98. # time (in words) since act took place
  99. # @return [String]
  100. 1 def elapsed_time
  101. DateTime.new(acted_at).distance_of_time_in_words_to_now
  102. end
  103. # act's actions on either the card itself or another card that includes it
  104. # @param card [Card]
  105. # @return [Array of Actions]
  106. 1 def actions_affecting card
  107. 51 actions.select do |action|
  108. 81 (card.id == action.card_id) ||
  109. card.nestee_ids.include?(action.card_id)
  110. end
  111. end
  112. 1 private
  113. # used by before filter
  114. 1 def assign_actor
  115. 267 self.actor_id ||= Auth.current_id
  116. end
  117. end
  118. end

card-mod-history/lib/card/act/act_renderer.rb

89.89% lines covered

89 relevant lines. 80 lines covered and 9 lines missed.
    
  1. 1 class Card
  2. 1 class Act
  3. 1 class ActRenderer
  4. 1 def initialize format, act, args
  5. 2 @format = format
  6. 2 @act = act
  7. 2 @act_card = act.card
  8. 2 @args = args
  9. 2 @card = @format.card
  10. 2 @context = @args[:act_context]
  11. end
  12. 1 include ::Bootstrapper
  13. 1 def method_missing method_name, *args, &block
  14. 44 if block_given?
  15. 8 @format.send(method_name, *args, &block)
  16. else
  17. 36 @format.send(method_name, *args)
  18. end
  19. end
  20. 1 def respond_to_missing? method_name, _include_private=false
  21. @format.respond_to? method_name
  22. end
  23. 1 def render
  24. 2 return "" unless @act_card
  25. 2 act_accordion
  26. end
  27. 1 def header
  28. #::Bootstrap.new(self).render do
  29. 2 bs_layout do
  30. 2 row xs: [10, 2] do
  31. 2 column do
  32. 2 html title
  33. 4 tag(:span, "text-muted pl-1 badge") { summary }
  34. end
  35. 2 column act_links, class: "text-right"
  36. end
  37. end
  38. # end
  39. end
  40. 1 def absolute_title
  41. 2 accordion_expand_link(@act_card.name)
  42. end
  43. 1 def details
  44. 2 approved_actions[0..20].map do |action|
  45. 2 Action::ActionRenderer.new(@format, action, action_header?,
  46. :summary).render
  47. end.join
  48. end
  49. 1 def summary
  50. 2 %i[create update delete draft].map do |type|
  51. 8 next unless count_types[type].positive?
  52. 2 "#{@format.action_icon type}<small> #{count_types[type]}</small>"
  53. end.compact.join "<small class='text-muted'> | </small>"
  54. end
  55. 1 def act_links
  56. [
  57. 2 link_to_history,
  58. 2 (link_to_act_card unless @act_card.trash)
  59. ].compact.join " "
  60. end
  61. 1 def link_to_act_card
  62. 2 link_to_card @act_card, icon_tag(:new_window), class: "_stop_propagation"
  63. end
  64. 1 def link_to_history
  65. 2 link_to_card @act_card, icon_tag(:history),
  66. path: { view: :history, look_in_trash: true },
  67. class: "_stop_propagation",
  68. rel: "nofollow"
  69. end
  70. 1 def approved_actions
  71. 6 @approved_actions ||= actions.select { |a| a.card&.ok?(:read) }
  72. # FIXME: should not need to test for presence of card here.
  73. end
  74. 1 def action_header?
  75. 2 true
  76. # @action_header ||= approved_actions.size != 1 ||
  77. # approved_actions[0].card_id != @format.card.id
  78. end
  79. 1 def count_types
  80. 10 @count_types ||=
  81. approved_actions.each_with_object(
  82. 8 Hash.new { |h, k| h[k] = 0 }
  83. ) do |action, type_cnt|
  84. 2 type_cnt[action.action_type] += 1
  85. end
  86. end
  87. 1 def edited_ago
  88. 2 return "" unless @act.acted_at
  89. 2 "#{time_ago_in_words(@act.acted_at)} ago"
  90. end
  91. 1 def collapse_id
  92. 12 "act-id-#{@act.id}"
  93. end
  94. 1 def accordion_expand_link text
  95. 2 <<-HTML
  96. <a>
  97. #{text}
  98. </a>
  99. HTML
  100. end
  101. # TODO: change accordion API in bootstrap/helper.rb so that it can be used
  102. # here. The problem is that here we have extra links in the title
  103. # that are not supposed to expand the accordion
  104. 1 def act_accordion
  105. 2 context = @act.main_action.draft ? :warning : :default
  106. 2 <<-HTML
  107. <div class="card card-#{context} nodblclick">
  108. #{act_accordion_panel}
  109. </div>
  110. HTML
  111. end
  112. 1 def accordion_expand_options
  113. {
  114. 2 "data-toggle" => "collapse",
  115. "data-target" => ".#{collapse_id}",
  116. "aria-expanded" => true,
  117. "aria-controls" => collapse_id
  118. }
  119. end
  120. 1 def act_panel_options
  121. 2 { class: "card-header", role: "tab", id: "heading-#{collapse_id}" }
  122. end
  123. 1 def act_accordion_panel
  124. 2 act_accordion_heading + act_accordion_body
  125. end
  126. 1 def act_accordion_heading
  127. 2 wrap_with :div, act_panel_options.merge(accordion_expand_options) do
  128. 2 wrap_with(:h5, header, class: "mb-0") + subtitle
  129. end
  130. end
  131. 1 def act_accordion_body
  132. 2 wrap_with :div, id: collapse_id,
  133. class: "collapse #{collapse_id}",
  134. "data-parent": ".act-accordion-group" do
  135. 2 wrap_with :div, details, class: "card-body"
  136. end
  137. end
  138. # Revert:
  139. # current update
  140. # Restore:
  141. # current deletion
  142. # Revert and Restore:
  143. # old deletions
  144. # blank:
  145. # current create
  146. # save as current:
  147. # not current, not deletion
  148. 1 def rollback_link
  149. 2 return unless card.ok? :update
  150. 2 wrap_with :div, class: "act-link collapse #{collapse_id} float-right" do
  151. 2 content_tag(:small, revert_link)
  152. # link_to "Save as current",
  153. # class: "slotter", remote: true,
  154. # method: :post, rel: "nofollow",
  155. # "data-slot-selector" => ".card-slot.history-view",
  156. # path: { action: :update, action_ids: prior,
  157. # view: :open, look_in_trash: true }
  158. end
  159. end
  160. 1 def deletion_act?
  161. act_type == :delete
  162. end
  163. 1 def act_type
  164. @act.main_action.action_type
  165. end
  166. 1 def show_or_hide_changes_link
  167. wrap_with :div, class: "act-link" do
  168. @format.link_to_view(
  169. :act, "#{@args[:hide_diff] ? 'Show' : 'Hide'} changes",
  170. path: { act_id: @args[:act].id, act_seq: @args[:act_seq],
  171. hide_diff: !@args[:hide_diff], action_view: :expanded,
  172. act_context: @args[:act_context], look_in_trash: true }
  173. )
  174. end
  175. end
  176. 1 def autosaved_draft_link opts={}
  177. text = opts.delete(:text) || "autosaved draft"
  178. opts[:path] = { edit_draft: true }
  179. add_class opts, "navbar-link"
  180. link_to_view :edit, text, opts
  181. end
  182. end
  183. end
  184. end

card-mod-history/lib/card/act/act_renderer/absolute_act_renderer.rb

100.0% lines covered

13 relevant lines. 13 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Act
  3. 1 class ActRenderer
  4. # Used for recent changes.
  5. # It shows all actions of an act
  6. 1 class AbsoluteActRenderer < ActRenderer
  7. 1 def title
  8. 2 absolute_title
  9. end
  10. 1 def subtitle
  11. 2 wrap_with :small do
  12. [
  13. 2 @format.link_to_card(@act.actor, nil, class: "_stop_propagation"),
  14. edited_ago,
  15. rollback_link
  16. ]
  17. end
  18. end
  19. # FIXME: how do we know we need main here??
  20. 1 def revert_link
  21. 2 revert_actions_link "revert to previous",
  22. { revert_to: :previous, revert_act: @act.id },
  23. "data-slot-selector": "#main > .card-slot"
  24. end
  25. 1 def actions
  26. 2 @act.actions
  27. end
  28. end
  29. end
  30. end
  31. end

card-mod-history/lib/card/action.rb

91.14% lines covered

79 relevant lines. 72 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # An _action_ is a group of {Card::Change changes} to a single {Card card}
  4. # that is recorded during an {Card::Act act}.
  5. # Together, {Act acts}, {Action actions}, and {Change changes} comprise a
  6. # comprehensive {Card card} history tracking system.
  7. #
  8. # For example, if a given web submission changes both the name and type of
  9. # a given card, that would be recorded as one {Action action} with two
  10. # {Change changes}. If there are multiple cards changed, each card would
  11. # have its own {Action action}, but the whole submission would still comprise
  12. # just one single {Act act}.
  13. #
  14. # An {Action} records:
  15. #
  16. # * the _card_id_ of the {Card card} acted upon
  17. # * the _card_act_id_ of the {Card::Act act} of which the action is part
  18. # * the _action_type_ (create, update, or delete)
  19. # * a boolean indicated whether the action is a _draft_
  20. # * a _comment_ (where applicable)
  21. #
  22. 1 class Action < ApplicationRecord
  23. 1 include Differ
  24. 1 extend Admin
  25. 1 belongs_to :act, foreign_key: :card_act_id, inverse_of: :ar_actions
  26. 1 belongs_to :ar_card, foreign_key: :card_id, inverse_of: :actions, class_name: "Card"
  27. 1 has_many :card_changes, foreign_key: :card_action_id,
  28. inverse_of: :action,
  29. dependent: :delete_all,
  30. class_name: "Card::Change"
  31. 1 belongs_to :super_action, class_name: "Action", inverse_of: :sub_actions
  32. 1 has_many :sub_actions, class_name: "Action", inverse_of: :super_action
  33. 1 scope :created_by, lambda { |actor_id|
  34. 210 joins(:act).where "card_acts.actor_id = ?", actor_id
  35. }
  36. # these are the three possible values for action_type
  37. 1 TYPE_OPTIONS = %i[create update delete].freeze
  38. 1 after_save :expire
  39. 1 class << self
  40. # retrieve action from cache if available
  41. # @param id [id of Action]
  42. # @return [Action, nil]
  43. 1 def fetch id
  44. 74 cache.fetch id.to_s do
  45. 18 find id.to_i
  46. end
  47. end
  48. # cache object for actions
  49. # @return [Card::Cache]
  50. 1 def cache
  51. 736 Card::Cache[Action]
  52. end
  53. 1 def all_with_cards
  54. joins :ar_card
  55. end
  56. 1 def all_viewable
  57. all_with_cards.where Query::CardQuery.viewable_sql
  58. end
  59. end
  60. # each action is associated with on and only one card
  61. # @return [Card]
  62. 1 def card
  63. 698 Card.fetch card_id, look_in_trash: true
  64. # I'm not sure what the rationale for the following was/is, but it was causing
  65. # problems in cases where slot attributes are overridden (eg see #wrap_data in
  66. # sources on wikirate). The problem is the format object had the set modules but
  67. # the card didn't.
  68. #
  69. # My guess is that the need for the following had something to do with errors
  70. # associated with changed types. If so, the solution probably needs to handle
  71. # including the set modules associated with the type at the time of the action
  72. # rather than including no set modules at all.
  73. #
  74. # What's more, we _definitely_ don't want to hard code special behavior for
  75. # specific types in here!
  76. # , skip_modules: true
  77. # return res unless res && res.type_id.in?([Card::FileID, Card::ImageID])
  78. # res.include_set_modules
  79. end
  80. # remove action from action cache
  81. 1 def expire
  82. 546 self.class.cache.delete id.to_s
  83. end
  84. # assign action_type (create, update, or delete)
  85. # @param value [Symbol]
  86. # @return [Integer]
  87. 1 def action_type= value
  88. 362 write_attribute :action_type, TYPE_OPTIONS.index(value)
  89. end
  90. # retrieve action_type (create, update, or delete)
  91. # @return [Symbol]
  92. 1 def action_type
  93. 615 return :draft if draft
  94. 615 TYPE_OPTIONS[read_attribute(:action_type)]
  95. end
  96. 1 def previous_action
  97. Card::Action.where("id < ? AND card_id = ?", id, card_id).last
  98. end
  99. # value set by action's {Change} to given field
  100. # @see #interpret_field #interpret_field for field param
  101. # @see #interpret_value #interpret_value for return values
  102. 1 def value field
  103. 279 return unless (change = change field)
  104. 141 interpret_value field, change.value
  105. end
  106. # value of field set by most recent {Change} before this one
  107. # @see #interpret_field #interpret_field for field param
  108. # @see #interpret_field #interpret_field for field param
  109. 1 def previous_value field
  110. 8 return if action_type == :create
  111. 4 return unless (previous_change = previous_change field)
  112. 4 interpret_value field, previous_change.value
  113. end
  114. # action's {Change} object for given field
  115. # @see #interpret_field #interpret_field for field param
  116. # @return [Change]
  117. 1 def change field
  118. 279 changes[interpret_field field]
  119. end
  120. # most recent change to given field before this one
  121. # @see #interpret_field #interpret_field for field param
  122. # @return [Change]
  123. 1 def previous_change field
  124. 4 return nil if action_type == :create
  125. 4 field = interpret_field field
  126. 4 if @previous_changes&.key?(field)
  127. @previous_changes[field]
  128. else
  129. 4 @previous_changes ||= {}
  130. 4 @previous_changes[field] = card.last_change_on field, before: self
  131. end
  132. end
  133. 1 def all_changes
  134. 89 self.class.cache.fetch("#{id}-changes") do
  135. # using card_changes causes caching problem
  136. 30 Card::Change.where(card_action_id: id).to_a
  137. end
  138. end
  139. # all action {Change changes} in hash form. { field1: Change1 }
  140. # @return [Hash]
  141. 1 def changes
  142. 279 @changes ||=
  143. 57 if sole?
  144. 25 current_changes
  145. else
  146. 32 all_changes.each_with_object({}) do |change, hash|
  147. 25 hash[change.field.to_sym] = change
  148. end
  149. end
  150. end
  151. # all changed values in hash form. { field1: new_value }
  152. 1 def changed_values
  153. @changed_values ||= changes.each_with_object({}) do |(key, change), h|
  154. h[key] = change.value
  155. end
  156. end
  157. # @return [Hash]
  158. 1 def current_changes
  159. 25 return {} unless card
  160. 25 @current_changes ||=
  161. Card::Change::TRACKED_FIELDS.each_with_object({}) do |field, hash|
  162. 150 hash[field.to_sym] = Card::Change.new field: field,
  163. value: card.send(field),
  164. card_action_id: id
  165. end
  166. end
  167. # translate field into fieldname as referred to in database
  168. # @see Change::TRACKED_FIELDS
  169. # @param field [Symbol] can be :type_id, :cardtype, :db_content, :content,
  170. # :name, :trash
  171. # @return [Symbol]
  172. 1 def interpret_field field
  173. 283 case field
  174. 50 when :content then :db_content
  175. 46 when :cardtype then :type_id
  176. 187 else field.to_sym
  177. end
  178. end
  179. # value in form prescribed for specific field name
  180. # @param value [value of {Change}]
  181. # @return [Integer] for :type_id
  182. # @return [String] for :name, :db_content, :content, :cardtype
  183. # @return [True/False] for :trash
  184. 1 def interpret_value field, value
  185. 145 case field.to_sym
  186. when :type_id
  187. value&.to_i
  188. when :cardtype
  189. 22 Card.fetch_name(value&.to_i)
  190. 123 else value
  191. end
  192. end
  193. 1 def sole?
  194. 57 all_changes.empty? &&
  195. 32 (action_type == :create || Card::Action.where(card_id: card_id).count == 1)
  196. end
  197. end
  198. end

card-mod-history/lib/card/action/action_renderer.rb

87.23% lines covered

47 relevant lines. 41 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. 1 class Action
  3. 1 class ActionRenderer
  4. 1 attr_reader :action, :header
  5. 1 def initialize format, action, header=true, action_view=:summary, hide_diff=false
  6. 2 @format = format
  7. 2 @action = action
  8. 2 @header = header
  9. 2 @action_view = action_view
  10. 2 @hide_diff = hide_diff
  11. end
  12. 1 include ::Bootstrapper
  13. 1 def method_missing method_name, *args, &block
  14. 22 if block_given?
  15. @format.send(method_name, *args, &block)
  16. else
  17. 22 @format.send(method_name, *args)
  18. end
  19. end
  20. 1 def respond_to_missing? method_name, _include_private=false
  21. @format.respond_to? method_name
  22. end
  23. 1 def render
  24. 2 classes = @format.classy("action-list")
  25. 2 bs_layout container: true, fluid: true do
  26. 2 row do
  27. 2 html <<-HTML
  28. <ul class="#{classes} w-100">
  29. <li class="#{action.action_type}">
  30. #{action_panel}
  31. </li>
  32. </ul>
  33. HTML
  34. end
  35. end
  36. end
  37. 1 def action_panel
  38. 2 bs_panel do
  39. 2 if header
  40. 2 heading do
  41. 2 div type_diff, class: "float-right"
  42. 2 div name_diff
  43. end
  44. end
  45. 2 body do
  46. 2 content_diff
  47. end
  48. end
  49. end
  50. 1 def name_diff
  51. 2 if @action.card == @format.card
  52. 2 name_changes
  53. else
  54. link_to_view(
  55. :related, name_changes,
  56. path: { slot: { items: { view: "history", nest_name: @action.card.name } } },
  57. # "data-slot-selector" => ".card-slot.history-view"
  58. )
  59. end
  60. end
  61. 1 def content_diff
  62. 2 return @action.raw_view if @action.action_type == :delete
  63. 2 @format.subformat(@action.card).render_action_summary action_id: @action.id
  64. end
  65. 1 def type_diff
  66. 2 return "" unless @action.new_type?
  67. @hide_diff ? @action.value(:cardtype) : @action.cardtype_diff
  68. end
  69. 1 def name_changes
  70. 2 return old_name unless @action.new_name?
  71. @hide_diff ? new_name : Card::Content::Diff.complete(old_name, new_name)
  72. end
  73. 1 def old_name
  74. 2 (name = @action.previous_value :name) && title_in_context(name)
  75. end
  76. 1 def new_name
  77. title_in_context @action.value(:name)
  78. end
  79. end
  80. end
  81. end

card-mod-history/lib/card/action/admin.rb

31.58% lines covered

19 relevant lines. 6 lines covered and 13 lines missed.
    
  1. 1 class Card
  2. 1 class Action
  3. # methods for administering card actions
  4. 1 module Admin
  5. # permanently delete all {Action actions} not associated with a {Card}
  6. 1 def delete_cardless
  7. left_join = "LEFT JOIN cards ON card_actions.card_id = cards.id"
  8. joins(left_join).where("cards.id IS NULL").delete_all
  9. end
  10. # permanently delete all {Action actions} associate with non-current
  11. # {Change changes}
  12. 1 def delete_old
  13. Card::Change.delete_all
  14. Card.find_each(&:delete_old_actions)
  15. Card::Act.delete_actionless
  16. end
  17. # If an act is given then all remaining actions will be attached to that act.
  18. # Otherwise the actions keep their acts.
  19. 1 def make_current_state_the_initial_state act=nil
  20. Card::Change.delete_all
  21. Card.find_each(&:delete_old_actions)
  22. action_update = { action_type: Card::Action::TYPE_OPTIONS.index(:create) }
  23. action_update[:card_act_id] = act.id if act
  24. Card::Action.update_all action_update
  25. if act
  26. Card::Act.where("id != :id", id: act.id).delete_all
  27. else
  28. Card::Act.delete_actionless
  29. end
  30. end
  31. end
  32. end
  33. end

card-mod-history/lib/card/action/differ.rb

82.5% lines covered

40 relevant lines. 33 lines covered and 7 lines missed.
    
  1. 1 class Card
  2. 1 class Action
  3. # a collection of methods for comparing actions
  4. 1 module Differ
  5. # compare action's name value with previous name value
  6. # @return [rendered diff]
  7. 1 def name_diff opts={}
  8. return unless new_name?
  9. diff_object(:name, opts).complete
  10. end
  11. # does action change card's name?
  12. # @return [true/false]
  13. 1 def new_name?
  14. 2 !value(:name).nil?
  15. end
  16. # @return [rendered diff]
  17. # compare action's cardtype value with previous cardtype value
  18. 1 def cardtype_diff opts={}
  19. return unless new_type?
  20. diff_object(:cardtype, opts).complete
  21. end
  22. # does action change card's type?
  23. # @return [true/false]
  24. 1 def new_type?
  25. 2 !value(:type_id).nil?
  26. end
  27. # @return [rendered diff]
  28. # compare action's content value with previous content value
  29. 1 def content_diff diff_type=:expanded, opts=nil
  30. 2 return unless new_content?
  31. 2 dobj = content_diff_object(opts)
  32. 2 diff_type == :summary ? dobj.summary : dobj.complete
  33. end
  34. # does action change card's content?
  35. # @return [true/false]
  36. 1 def new_content?
  37. 141 !value(:db_content).nil?
  38. end
  39. # test whether content was visibly removed
  40. # @return [true/false]
  41. 1 def red?
  42. content_diff_object.red?
  43. end
  44. # test whether content was visibly added
  45. # @return [true/false]
  46. 1 def green?
  47. content_diff_object.green?
  48. end
  49. 1 def raw_view content=nil
  50. 4 original_content = card.db_content
  51. 4 card.db_content = content || value(:db_content)
  52. 4 card.format.render_raw
  53. ensure
  54. 4 card.db_content = original_content
  55. end
  56. 1 def summary_diff_omits_content?
  57. 2 content_diff_object.summary_omits_content?
  58. end
  59. 1 private
  60. 1 def diff_object field, opts
  61. Card::Content::Diff.new previous_value(field), value(field), opts
  62. end
  63. 1 def content_diff_object opts=nil
  64. 4 @diff ||= begin
  65. 2 diff_args = opts || card.include_set_modules.diff_args
  66. 2 previous_value = previous_value(:content)
  67. 2 previous = previous_value ? raw_view(previous_value) : ""
  68. 2 current = raw_view
  69. 2 Card::Content::Diff.new previous, current, diff_args
  70. end
  71. end
  72. end
  73. end
  74. end

card-mod-history/lib/card/change.rb

77.78% lines covered

18 relevant lines. 14 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 require 'activerecord-import'
  3. 1 class Card
  4. # A _change_ is an alteration to a card's name, type, content, or trash state.
  5. # Together, {Act acts}, {Action actions}, and {Change changes} comprise a
  6. # comprehensive {Card card} history tracking system.
  7. #
  8. # For example, if a given web submission changes both the name and type of
  9. # card, that would be recorded as one {Action action} with two
  10. # {Change changes}.
  11. #
  12. # A {Change} records:
  13. #
  14. # * the _field_ changed
  15. # * the new _value_ of that field
  16. # * the {Action action} of which the change is part
  17. #
  18. 1 class Change < ApplicationRecord
  19. 1 belongs_to :action, foreign_key: :card_action_id,
  20. inverse_of: :card_changes
  21. # lists the database fields for which changes are recorded
  22. 1 TRACKED_FIELDS = %w[name type_id db_content trash left_id right_id].freeze
  23. 1 class << self
  24. # delete all {Change changes} not associated with an {Action action}
  25. # (janitorial)
  26. 1 def delete_actionless
  27. joins(
  28. "LEFT JOIN card_actions "\
  29. "ON card_changes.card_action_id = card_actions.id "
  30. ).where(
  31. "card_actions.id is null"
  32. ).pluck_in_batches(:id) do |group_ids|
  33. # used to be .delete_all here, but that was failing on large dbs
  34. Rails.logger.info "deleting batch of changes"
  35. where("id in (#{group_ids.join ','})").delete_all
  36. end
  37. end
  38. # Change fields are recorded as integers. #field_index looks up the
  39. # integer associated with a given field name.
  40. # @param value [String, Symbol]
  41. # @return [Integer]
  42. 1 def field_index value
  43. 26 value.is_a?(Integer) ? value : TRACKED_FIELDS.index(value.to_s)
  44. end
  45. # look up changes based on field name
  46. # @param value [String, Symbol]
  47. # @return [Change]
  48. 1 def find_by_field_name value
  49. find_by_field field_index(value)
  50. end
  51. end
  52. # set field value (integer)
  53. # @param value [String, Symbol]
  54. 1 def field= value
  55. 361 write_attribute(:field, TRACKED_FIELDS.index(value.to_s))
  56. end
  57. # retrieve field name
  58. # @return [String]
  59. 1 def field
  60. 25 TRACKED_FIELDS[read_attribute(:field)]
  61. end
  62. end
  63. end

card-mod-machines/lib/card/machine.rb

57.89% lines covered

19 relevant lines. 11 lines covered and 8 lines missed.
    
  1. 1 class Card
  2. 1 module Machine
  3. 1 REFRESHED = "MACHINE_ASSETS_REFRESHED".freeze
  4. 1 class << self
  5. 1 def refresh_script_and_style
  6. 414 return unless refresh_script_and_style?
  7. Card.fetch(:all, :script)&.update_if_source_file_changed
  8. Card.fetch(:all, :style)&.update_if_source_file_changed
  9. end
  10. 1 private
  11. 1 def refresh_script_and_style?
  12. 414 case Cardio.config.machine_refresh
  13. when :eager then true
  14. when :cautious then cautious_refresh?
  15. 414 when :never then false
  16. else
  17. raise Card::Error,
  18. "unknown option for machine_refresh: #{Cardio.config.machine_refresh}"
  19. end
  20. end
  21. # only refresh when cache was cleared
  22. 1 def cautious_refresh?
  23. return false unless Card::Cache.persistent_cache
  24. return false if Card.cache.read REFRESHED
  25. Card.cache.write REFRESHED, true
  26. end
  27. end
  28. end
  29. end

card-mod-platypus/lib/card/mod/platypus.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 require "fog/aws"

card/lib/application_job.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 class ApplicationJob < ActiveJob::Base
  2. end

card/lib/application_record.rb

100.0% lines covered

2 relevant lines. 2 lines covered and 0 lines missed.
    
  1. 1 class ApplicationRecord < ActiveRecord::Base
  2. 1 self.abstract_class = true
  3. end

card/lib/card.rb

100.0% lines covered

20 relevant lines. 20 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 ActiveSupport.run_load_hooks(:before_card, self)
  3. # Cards are wiki-inspired building blocks.
  4. #
  5. # This documentation is for developers who want to understand:
  6. #
  7. # 1. how ruby Card objects work, and
  8. # 2. how to extend them.
  9. #
  10. # It assumes that you've already read the introductory text in {file:README.md}.
  11. #
  12. # Throughout this document we will refer to @card as an instance of a Card object.
  13. #
  14. # ## Names
  15. #
  16. # There are four important card identifiers, sometimes called "marks". Every card has a
  17. # unique _name_, _key_, and _id_. Some cards also have a _codename_.
  18. #
  19. # @card.name # The name, a Card::Name object, is the most recognizable card
  20. # mark.
  21. # @card.key # The key, a String, is a simple lower-case name variant.
  22. # @card.id # The id is an Integer.
  23. # @card.codename # The codename, a Symbol, is the name by which a card can be
  24. # referred to in code.
  25. #
  26. # All names with the same key (including the key itself) are considered variants of each
  27. # other. No two cards can have names with the same key. {Card::Name} objects inherit from
  28. # Strings but add many other methods for common card name patterns, eg
  29. # `"A+B".to_name.right => "B"`.
  30. #
  31. # Setting a card's name, eg `@card.name = "New Name"`, will automatically update the key.
  32. #
  33. # - {Card::Name More on names.}
  34. # - {Card::Codename More on codenames.}
  35. #
  36. # ## Type
  37. #
  38. # Every card has a type, and every type itself has an associated card. For example,
  39. # _Paula_'s type might be _User_, so there is also a _User_ card.
  40. #
  41. # The type may be accessed in several ways:
  42. #
  43. # @card.type_id # returns id of type card [Integer]
  44. # @card.type_name # returns name of type card [Card::Name]
  45. # @card.type_code # returns codename of type card [Symbol]
  46. # @card.type_card # returns Cardtype card associated with @card's type [Card]
  47. #
  48. # {file:mod/core/set/all/type.rb Common type methods}
  49. #
  50. # ## Content
  51. #
  52. # There are two primary methods for accessing a card's content:
  53. #
  54. # @card.db_content # the content as it appears in the database
  55. # @card.content # the "official" content, which may be different from
  56. # db_content when db_content is overridden with a structure rule.
  57. #
  58. # {Card::Content Processing card content}
  59. #
  60. # {file:mod/core/set/all/content.rb Common content methods}
  61. #
  62. # ## Fetch
  63. #
  64. # The two main ways to retrieve cards are fetching (retrieving cards one at a time) and
  65. # querying (retrieving lists of cards). More on querying below.
  66. #
  67. # Any of the above marks (name, key, id, codename) can be used to fetch a card, eg:
  68. #
  69. # @card = Card.fetch "Garden" # returns the card with the name "Garden" (or, more
  70. # precisely, with the key "garden")
  71. # @card = Card.fetch 100 # returns the card with the id 100
  72. # @card = Card.fetch :help # returns the card with the codename help
  73. #
  74. # The fetch API will first try to find the card in the cache and will only look in the
  75. # database if necessary.
  76. #
  77. # {file:mod/core/set/all/fetch.rb More on fetching.}
  78. #
  79. # ## Query
  80. #
  81. # Card queries find and return lists of cards, eg:
  82. #
  83. # Card.search type_id: 4 # returns an Array of cards with the type_id of 4.
  84. #
  85. # {Card::Query More on queries}
  86. #
  87. # ## Views and Events
  88. #
  89. # Views and events are a _Shark's_ primary tools for manipulating cards. Views customize
  90. # card presentation, while events customize card transactions. Or, if you like, views
  91. # and events respectively alter cards in _space_ and _time_.
  92. #
  93. # Both views and events are defined in {Card::Mod mods}, short for modules or
  94. # modifications.
  95. #
  96. # {Card::Set::Format::AbstractFormat More on views}
  97. #
  98. # {Card::Set::Event::Api More on events}
  99. #
  100. # ## Accounts and Permissions
  101. #
  102. # Card code is always executed in the context of a given user account. Permissions for
  103. # that account are automatically checked when running a query, performing an action, or
  104. # rendering a view. A typical query, for example, can only return cards that the current
  105. # user has permission to read.
  106. #
  107. # You can see the current user with `Card::Auth.current`. The permissions of a proxy user
  108. # can be temporarily assumed using `Card::Auth#as`.
  109. #
  110. # {Card::Auth More on accounts}
  111. 1 class Card < ApplicationRecord
  112. 1 extend Mark
  113. 1 extend Dirty::MethodFactory
  114. 1 include Dirty
  115. 1 include DirtyNames
  116. 1 include Director::CardMethods
  117. 1 Card::Cache # trigger autoload
  118. 1 has_many :references_in, class_name: :Reference, foreign_key: :referee_id
  119. 1 has_many :references_out, class_name: :Reference, foreign_key: :referer_id
  120. 23 has_many :acts, -> { order :id }
  121. 204 has_many :actions, -> { where(draft: [nil, false]).order :id }
  122. 421 has_many :drafts, -> { where(draft: true).order :id }, class_name: :Action
  123. 1 cattr_accessor :set_patterns, :action_specific_attributes, :set_specific_attributes
  124. 1 self.set_patterns = []
  125. 1 self.action_specific_attributes = [
  126. :supercard,
  127. :superleft,
  128. :action,
  129. :current_action,
  130. :last_action_id_before_edit,
  131. :skip, # skip event(s) for all cards in act
  132. :skip_in_action, # skip event for just this card
  133. :trigger, # trigger event(s) for all cards in act
  134. :trigger_in_action, # trigger event for just this card
  135. :comment, # obviated soon
  136. # TODO: refactor following to use skip/trigger
  137. :update_referers, # wrong mechanism for this
  138. :update_all_users, # if the above is wrong then this one too
  139. :silent_change # and this probably too
  140. ]
  141. 1 attr_accessor(*action_specific_attributes)
  142. 1 define_callbacks :select_action, :show_page, :act
  143. 1 ActiveSupport.run_load_hooks :card, self
  144. end
  145. 1 ActiveSupport.run_load_hooks :after_card, self

card/lib/card/auth.rb

88.89% lines covered

18 relevant lines. 16 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # Singleton methods for account authentication and contextualization.
  4. #
  5. # Manages current user,
  6. # "as" user, and password verification.
  7. 1 module Auth
  8. 1 extend Permissions
  9. 1 extend Proxy
  10. 1 extend Setup
  11. 1 extend Current
  12. 1 @as_card = @as_id = @current_id = @current = nil
  13. 1 class << self
  14. # authenticate a user by their login name and unencrypted password.
  15. # @param email [String]
  16. # @param password [String]
  17. # @return [+*account card, nil]
  18. 1 def authenticate email, password
  19. 71 account = Auth.find_account_by_email email
  20. case
  21. 71 when !account then nil
  22. when !account.active? then nil
  23. when Card.config.no_authentication then account
  24. 71 when password_valid?(account, password.strip) then account
  25. end
  26. end
  27. # check whether password is correct for account card
  28. # @param account [+*account card]
  29. # @param password [String]
  30. 1 def password_valid? account, password
  31. 71 account.password == encrypt(password, account.salt)
  32. end
  33. # encrypt password string with the given salt.
  34. # @return [SHA1 String]
  35. 1 def encrypt password, salt
  36. 74 Digest::SHA1.hexdigest "#{salt}--#{password}--"
  37. end
  38. end
  39. end
  40. end

card/lib/card/auth/current.rb

90.41% lines covered

73 relevant lines. 66 lines covered and 7 lines missed.
    
  1. 1 class Card
  2. 1 module Auth
  3. # methods for setting current account
  4. 1 module Current
  5. # set current user in process and session
  6. 1 def signin cardish
  7. 524 signin_id = Card.id(cardish) || Card::AnonymousID
  8. 524 self.current_id = signin_id
  9. 524 set_session_user signin_id
  10. end
  11. # current user is not anonymous
  12. # @return [true/false]
  13. 1 def signed_in?
  14. 1482 current_id != Card::AnonymousID
  15. end
  16. # id of current user card.
  17. # @return [Integer]
  18. 1 def current_id
  19. 18870 @current_id ||= Card::AnonymousID
  20. end
  21. # current accounted card (must have +\*account)
  22. # @return [Card]
  23. 1 def current
  24. 922 if @current && @current.id == current_id
  25. 704 @current
  26. else
  27. 218 @current = Card[current_id]
  28. end
  29. end
  30. 1 def current_roles
  31. 784 @current_roles ||= [Card.fetch_name(:anyone_signed_in),
  32. current.fetch(:roles)&.item_names].flatten.compact
  33. end
  34. 1 def serialize
  35. 95 { as_id: as_id, current_id: current_id }
  36. end
  37. # @param auth_data [Integer|Hash] user id, user name, or a hash
  38. # @option auth_data [Integer] current_id
  39. # @option auth_data [Integer] as_id
  40. 1 def with auth_data
  41. 41 if auth_data.is_a?(Integer) || auth_data.is_a?(String)
  42. auth_data = { current_id: Card.id(auth_data) }
  43. end
  44. 41 tmp_current_id = current_id
  45. 41 tmp_as_id = as_id
  46. 41 tmp_current = @current
  47. 41 tmp_as_card = @as_card
  48. 41 tmp_current_roles = @current_roles
  49. # resets @as and @as_card
  50. 41 self.current_id = auth_data[:current_id]
  51. 41 @as_id = auth_data[:as_id] if auth_data[:as_id]
  52. 41 yield
  53. ensure
  54. 41 @current_id = tmp_current_id
  55. 41 @as_id = tmp_as_id
  56. 41 @current = tmp_current
  57. 41 @as_card = tmp_as_card
  58. 41 @current_roles = tmp_current_roles
  59. end
  60. # get session object from Env
  61. # return [Session]
  62. 1 def session
  63. 966 Card::Env.session
  64. end
  65. # set current from token, api_key, or session
  66. 1 def signin_with opts={}
  67. 444 if opts[:token]
  68. 2 signin_with_token opts[:token]
  69. 442 elsif opts[:api_key]
  70. signin_with_api_key opts[:api_key]
  71. else
  72. 442 signin_with_session
  73. end
  74. end
  75. # set the current user based on token
  76. 1 def signin_with_token token
  77. 2 payload = Token.validate! token
  78. 2 signin payload[:anonymous] ? Card::AnonymousID : payload[:user_id]
  79. end
  80. # set the current user based on api_key
  81. 1 def signin_with_api_key api_key
  82. account = find_account_by_api_key api_key
  83. unless account&.validate_api_key! api_key
  84. raise Card::Error::PermissionDenied, "API key authentication failed"
  85. end
  86. signin account.left_id
  87. end
  88. # get :user id from session and set Auth.current_id
  89. 1 def signin_with_session
  90. 442 card_id = session_user
  91. 442 signin(card_id && Card.exists?(card_id) ? card_id : nil)
  92. end
  93. # find +\*account card by +\*api card
  94. # @param api_key [String]
  95. # @return [+*account card, nil]
  96. 1 def find_account_by_api_key api_key
  97. find_account_by :api_key, api_key.strip
  98. end
  99. # find +\*account card by +\*email card
  100. # @param email [String]
  101. # @return [+*account card, nil]
  102. 1 def find_account_by_email email
  103. 72 find_account_by :email, email.strip.downcase
  104. end
  105. # general pattern for finding +\*account card based on field cards
  106. # @param fieldcode [Symbol] code of account field
  107. # @param value [String] content of field
  108. # @return [+*account card, nil]
  109. 1 def find_account_by fieldcode, value
  110. 72 Auth.as_bot do
  111. 72 Card.search({ right_id: Card::AccountID,
  112. right_plus: [Card::Codename.id(fieldcode), { content: value }] },
  113. "find +:account with +#{fieldcode} (#{value})").first
  114. end
  115. end
  116. 1 def session_user
  117. 442 session[session_user_key]
  118. end
  119. 1 def set_session_user card_id
  120. 524 session[session_user_key] = card_id
  121. end
  122. 1 def session_user_key
  123. 966 "user_#{database.underscore}".to_sym
  124. end
  125. 1 def database
  126. 966 Rails.configuration.database_configuration.dig Rails.env, "database"
  127. end
  128. # set the id of the current user.
  129. 1 def current_id= card_id
  130. 566 @current = @as_id = @as_card = @current_roles = nil
  131. 566 card_id = card_id.to_i if card_id.present?
  132. 566 @current_id = card_id
  133. end
  134. end
  135. end
  136. end

card/lib/card/auth/permissions.rb

96.88% lines covered

32 relevant lines. 31 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Auth
  3. # singleton permission methods
  4. 1 module Permissions
  5. # user has "root" permissions
  6. # @return [true/false]
  7. 1 def always_ok?
  8. 6082 usr_id = as_id
  9. 6082 case usr_id
  10. 1090 when Card::WagnBotID then true # cannot disable
  11. when nil then false
  12. else
  13. 4992 always_ok_usr_id? usr_id
  14. end
  15. end
  16. # specified user has root permission
  17. # @param usr_id [Integer]
  18. # @return [true/false]
  19. 1 def always_ok_usr_id? usr_id, force_cache_update=false
  20. 4992 always = always_cache
  21. 4992 if always[usr_id].nil? || force_cache_update
  22. 88 update_always_cache usr_id, admin?(usr_id)
  23. else
  24. 4904 always[usr_id]
  25. end
  26. end
  27. 1 def update_always_cache usr_id, value
  28. 89 always = always_cache
  29. 89 always = always.dup if always.frozen?
  30. 89 always[usr_id] = value
  31. 89 Card.cache.write "ALWAYS", always
  32. 89 value
  33. end
  34. 1 def always_cache
  35. 5081 Card.cache.read("ALWAYS") || {}
  36. end
  37. # list of names of cardtype cards that current user has perms to create
  38. # @return [Array of strings]
  39. 1 def createable_types
  40. type_names =
  41. 12 Auth.as_bot do
  42. 12 Card.search(
  43. { type: Card::CardtypeID, return: :name,
  44. not: { codename: ["in"] + Card.config.non_createable_types } },
  45. "find createable types"
  46. )
  47. end
  48. 12 type_names.select do |name|
  49. 531 Card.new(type: name).ok? :create
  50. end.sort
  51. end
  52. # test whether user is an administrator
  53. # @param user_id [Integer]
  54. # @return [true/false]
  55. 1 def admin? user_id
  56. 88 has_role? user_id, Card::AdministratorID
  57. end
  58. 1 def has_role? user_id, role_id
  59. 88 return false unless user_id && role_id
  60. 88 Card[user_id].all_enabled_roles.include? role_id
  61. end
  62. end
  63. end
  64. end

card/lib/card/auth/proxy.rb

96.3% lines covered

27 relevant lines. 26 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Auth
  3. # mechanism for assuming permissions of another user.
  4. 1 module Proxy
  5. # operate with the permissions of another "proxy" user
  6. 1 def as given_user
  7. 2409 tmp_id = @as_id
  8. 2409 tmp_card = @as_card
  9. 2409 @as_id = get_user_id(given_user)
  10. 2409 @as_card = nil
  11. # we could go ahead and set as_card if given a card...
  12. 2409 @current_id = @as_id if @current_id.nil?
  13. 2409 return unless block_given?
  14. 2407 yield
  15. ensure
  16. 2409 if block_given?
  17. 2407 @as_id = tmp_id
  18. 2407 @as_card = tmp_card
  19. end
  20. end
  21. # operate with the permissions of WagnBot (administrator)
  22. 1 def as_bot &block
  23. 2311 as Card::WagnBotID, &block
  24. end
  25. # id of proxy user
  26. # @return [Integer]
  27. 1 def as_id
  28. 16698 @as_id || current_id
  29. end
  30. # proxy user card
  31. # @return [Card]
  32. 1 def as_card
  33. 2007 if @as_card && @as_card.id == as_id
  34. 1746 @as_card
  35. else
  36. 261 @as_card = Card[as_id]
  37. end
  38. end
  39. # get card id from args of unknown type
  40. # @todo replace with general mechanism, eg #quick_fetch
  41. 1 def get_user_id user
  42. 2409 case user
  43. when NilClass then nil
  44. 81 when Card then user.id
  45. 2328 when Integer then user
  46. else Card.fetch_id(user)
  47. end
  48. end
  49. end
  50. end
  51. end

card/lib/card/auth/setup.rb

89.29% lines covered

28 relevant lines. 25 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 module Auth
  3. # singleton methods for managing setup state
  4. 1 module Setup
  5. 1 NEEDS_SETUP = "NEEDS_SETUP".freeze
  6. # app is not totally set up yet
  7. # @return [true/false]
  8. 1 def needs_setup?
  9. 76 if @needs_setup == false || Card.cache.read(NEEDS_SETUP)&.to_s == "false"
  10. 70 @needs_setup = false
  11. else
  12. 6 needs_setup_if_no_accounts
  13. end
  14. end
  15. # for testing setup
  16. 1 def simulate_setup! mode=true
  17. 2 Card.cache.delete NEEDS_SETUP
  18. 2 @needs_setup = nil
  19. 2 @hidden_accounts = mode ? user_account_ids : nil
  20. end
  21. 1 def instant_account_activation
  22. simulate_needs_setup!
  23. yield
  24. ensure
  25. simulate_needs_setup! false
  26. end
  27. 1 private
  28. 1 def needs_setup_if_no_accounts
  29. 6 user_account_count.zero?.tap do |need|
  30. 6 Card.cache.write NEEDS_SETUP, false unless need
  31. end
  32. end
  33. 1 def user_account_ids
  34. 2 as_bot { Card.search user_account_cql.merge(return: :id) }
  35. end
  36. 1 def user_account_cql
  37. # every deck starts with two accounts: WagnBot and Anonymous
  38. 7 { right_id: Card::AccountID, creator_id: ["ne", Card::WagnBotID] }
  39. end
  40. 1 def user_account_count
  41. 6 cql = user_account_cql
  42. 6 cql[:not] = { id: ["in"].concat(@hidden_accounts) } if @hidden_accounts
  43. 12 as_bot { Card.count_by_cql cql }
  44. end
  45. end
  46. end
  47. end

card/lib/card/auth/token.rb

94.74% lines covered

19 relevant lines. 18 lines covered and 1 lines missed.
    
  1. 1 require "jwt"
  2. 1 class Card
  3. 1 module Auth
  4. # methods for setting current account
  5. 1 module Token
  6. 1 SECRET_KEY = Rails.application.secrets.secret_key_base.to_s
  7. 1 class << self
  8. 1 def encode user_id, extra_payload={}
  9. 4 payload = { user_id: user_id, exp: expiration }.merge(extra_payload)
  10. 4 JWT.encode payload, SECRET_KEY
  11. end
  12. # returns Hash if valid, String error message if not
  13. 1 def validate! token
  14. 2 payload = decode token
  15. 2 raise Card::Error::PermissionDenied, payload if payload.is_a? String
  16. 2 payload
  17. end
  18. 1 def decode token
  19. 4 decoded = JWT.decode(token, SECRET_KEY)[0]
  20. 4 HashWithIndifferentAccess.new decoded
  21. rescue JWT::DecodeError => error
  22. error.message
  23. end
  24. 1 def expiration
  25. 4 Card.config.token_expiry.from_now.to_i
  26. end
  27. end
  28. end
  29. end
  30. end

card/lib/card/cache.rb

77.33% lines covered

75 relevant lines. 58 lines covered and 17 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class << self
  4. 1 def cache
  5. 273172 Card::Cache[Card]
  6. end
  7. end
  8. # The {Cache} class manages and integrates {Temporary} and {Persistent}
  9. # caching. The {Temporary} cache is typically process- and request- specific
  10. # and is often "ahead" of the database; the {Persistent} cache is typically
  11. # shared across processes and tends to stay true to the database.
  12. #
  13. # Any ruby Class can declare and/or retrieve its own cache as follows:
  14. #
  15. # ```` Card::Cache[MyClass] ````
  16. #
  17. # Typically speaking, mod developers do not need to use the Cache classes
  18. # directly, because caching is automatically handled by Card#fetch
  19. #
  20. 1 class Cache
  21. 1 extend Card::Cache::Prepopulate
  22. 1 @@cache_by_class = {}
  23. 1 cattr_reader :cache_by_class
  24. 1 class << self
  25. 1 attr_accessor :no_renewal
  26. # create a new cache for the ruby class provided
  27. # @param klass [Class]
  28. # @return [{Card::Cache}]
  29. 1 def [] klass
  30. 354730 raise "nil klass" if klass.nil?
  31. 354730 cache_type = persistent_cache || nil
  32. 354730 cache_by_class[klass] ||= new class: klass, store: cache_type
  33. end
  34. 1 def persistent_cache
  35. 355305 return @persistent_cache unless @persistent_cache.nil?
  36. @persistent_cache =
  37. case
  38. 1 when ENV["NO_RAILS_CACHE"] then false
  39. 1 when Cardio.config.persistent_cache then Cardio.cache
  40. else false
  41. end
  42. end
  43. # clear the temporary caches and ensure we're using the latest stamp
  44. # on the persistent caches.
  45. 1 def renew
  46. 537 return if no_renewal
  47. 537 renew_persistent
  48. 537 cache_by_class.each_value do |cache|
  49. 2021 cache.soft.reset
  50. 2021 cache.hard&.renew
  51. end
  52. end
  53. 1 def renew_persistent
  54. 537 Card::Cache::Persistent.renew if persistent_cache
  55. end
  56. # reset standard cached for all classes
  57. 1 def reset
  58. 30 reset_hard
  59. 30 reset_soft
  60. end
  61. # reset all caches for all classes
  62. 1 def reset_all
  63. reset_hard
  64. reset_soft
  65. reset_other
  66. end
  67. # completely wipe out all caches, often including the Persistent cache of
  68. # other decks using the same mechanism.
  69. # Generally prefer {.reset_all}
  70. # @see .reset_all
  71. 1 def reset_global
  72. cache_by_class.each_value do |cache|
  73. cache.soft.reset
  74. cache.hard&.annihilate
  75. end
  76. reset_other
  77. end
  78. # reset the Persistent cache for all classes
  79. 1 def reset_hard
  80. 38 Card::Cache::Persistent.reset if persistent_cache
  81. 38 cache_by_class.each_value do |cache|
  82. 140 cache.hard&.reset
  83. end
  84. end
  85. # reset the Temporary cache for all classes
  86. 1 def reset_soft
  87. 178 cache_by_class.each_value { |cache| cache.soft.reset }
  88. end
  89. # reset Codename cache and delete tmp files
  90. # (the non-standard caches)
  91. 1 def reset_other
  92. Card::Codename.reset_cache
  93. Cardio.delete_tmp_files!
  94. end
  95. # generate a cache key from an object
  96. # @param obj [Object]
  97. # @return [String]
  98. 1 def obj_to_key obj
  99. case obj
  100. when Hash
  101. obj.sort.map { |key, value| "#{key}=>(#{obj_to_key(value)})" } * ","
  102. when Array
  103. obj.map { |value| obj_to_key(value) }
  104. else
  105. obj.to_s
  106. end
  107. end
  108. end
  109. 1 attr_reader :hard, :soft
  110. # Cache#new initializes a {Temporary}/soft cache, and -- if a :store opt
  111. # is provided -- a {Persistent}/hard cache
  112. # @param opts [Hash]
  113. # @option opts [Rails::Cache] :store
  114. # @option opts [Constant] :class
  115. 1 def initialize opts={}
  116. 4 @klass = opts[:class]
  117. 4 cache_by_class[@klass] = self
  118. 4 @hard = Persistent.new opts if opts[:store]
  119. 4 @soft = Temporary.new
  120. end
  121. # read cache value (and write to soft cache if missing)
  122. # @param key [String]
  123. 1 def read key
  124. 134957 @soft.read(key) ||
  125. 28475 (@hard && (ret = @hard.read(key)) && @soft.write(key, ret))
  126. end
  127. # write to hard (where applicable) and soft cache
  128. # @param key [String]
  129. # @param value
  130. 1 def write key, value
  131. 9763 @hard&.write key, value
  132. 9763 @soft.write key, value
  133. end
  134. # read and (if not there yet) write
  135. # @param key [String]
  136. 1 def fetch key, &block
  137. 108215 @soft.fetch(key) { @hard ? @hard.fetch(key, &block) : yield }
  138. end
  139. # delete specific cache entries by key
  140. # @param key [String]
  141. 1 def delete key
  142. 1513 @hard&.delete key
  143. 1513 @soft.delete key
  144. end
  145. # reset both caches (for a given Card::Cache instance)
  146. 1 def reset
  147. @hard&.reset
  148. @soft.reset
  149. end
  150. # test for the existence of the key in either cache
  151. # @return [true/false]
  152. 1 def exist? key
  153. @soft.exist?(key) || (@hard&.exist?(key))
  154. end
  155. end
  156. end

card/lib/card/cache/persistent.rb

89.06% lines covered

64 relevant lines. 57 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Cache
  4. # _Persistent_ (or _Hard_) caches closely mirror the database and are
  5. # intended to be altered only upon database alterations.
  6. #
  7. # Unlike the database, the persistent cache stores records of records that
  8. # have been requested but are missing or, in the case of some {Card cards},
  9. # "virtual", meaning that they follow known patterns but do not exist in the
  10. # database.
  11. #
  12. # Most persistent cache implementations cannot store objects with singleton
  13. # classes, therefore {Card cards} generally must have set_modules
  14. # re-included after retrieval from the persistent cache.
  15. #
  16. 1 class Persistent
  17. 1 attr_accessor :prefix
  18. 1 class << self
  19. # name of current database; used here to insure that different databases
  20. # are cached separately
  21. # TODO: find better home for this method
  22. 1 def database_name
  23. 580 @database_name ||= (cfg = Cardio.config) &&
  24. 1 (dbcfg = cfg.database_configuration) &&
  25. dbcfg[Rails.env]["database"]
  26. end
  27. 1 def stamp
  28. 1425 @stamp ||= Cardio.cache.fetch(stamp_key) { new_stamp }
  29. end
  30. # stamp generator
  31. 1 def new_stamp
  32. 183 Time.now.to_i.to_s(36) + rand(999).to_s(36)
  33. end
  34. 1 def stamp_key
  35. 576 "#{database_name}-stamp"
  36. end
  37. 1 def renew
  38. 537 @stamp = nil
  39. end
  40. 1 def reset
  41. 38 @stamp = new_stamp
  42. 38 Cardio.cache.write stamp_key, @stamp
  43. end
  44. end
  45. # @param opts [Hash]
  46. # @option opts [Rails::Cache] :store
  47. # @option opts [ruby Class] :class, typically ActiveRecord descendant
  48. # @option opts [String] :database
  49. 1 def initialize opts
  50. 4 @store = opts[:store]
  51. 4 @klass = opts[:class]
  52. 4 @class_key = @klass.to_s.to_name.key
  53. 4 @database = opts[:database] || self.class.database_name
  54. end
  55. # renew insures you're using the most current cache version by
  56. # reaffirming the stamp and prefix
  57. 1 def renew
  58. 2021 @stamp = nil
  59. 2021 @prefix = nil
  60. end
  61. # reset effectively clears the cache by setting a new stamp. However
  62. # unlike annihilate, it won't bother other apps using the same cache engine.
  63. 1 def reset
  64. 140 @stamp = self.class.new_stamp
  65. 140 @prefix = nil
  66. 140 Cardio.cache.write stamp_key, @stamp
  67. end
  68. # the nuclear option. can affect other applications sharing the same
  69. # cache engine. keep in mind mutually assured destruction.
  70. 1 def annihilate
  71. @store.clear
  72. end
  73. # the current time stamp. changing this value effectively resets
  74. # the cache. Note that Cardio.cache is a simple Rails::Cache, not
  75. # a Card::Cache object.
  76. 1 def stamp
  77. 1356 @stamp ||= Cardio.cache.fetch(stamp_key) { self.class.new_stamp }
  78. end
  79. # key for looking up the current stamp
  80. 1 def stamp_key
  81. 1424 "#{@database}-#{@class_key}-#{self.class.stamp}-stamp"
  82. end
  83. # prefix added to cache key to create a system-wide unique key
  84. 1 def prefix
  85. 67764 @prefix ||= "#{@database}-#{@class_key}-#{stamp}:"
  86. end
  87. # returns prefix/key
  88. # @param key [String]
  89. # @return [String]
  90. 1 def full_key key
  91. 67764 "#{prefix}/#{key}"
  92. end
  93. 1 def read key
  94. 28995 @store.read full_key(key)
  95. end
  96. # update an attribute of an object already in the cache
  97. # @param key [String]
  98. # @param attribute [String, Symbol]
  99. 1 def write_attribute key, attribute, value
  100. return value unless @store
  101. if (object = deep_read key)
  102. object.instance_variable_set "@#{attribute}", value
  103. write key, object
  104. end
  105. value
  106. end
  107. 1 def deep_read key
  108. 520 local_cache = @store.send :local_cache
  109. 520 local_cache&.clear
  110. 520 read key
  111. end
  112. 1 def read_attribute key, attribute
  113. 520 object = deep_read key
  114. 520 object.instance_variable_get "@#{attribute}"
  115. end
  116. 1 def write key, value
  117. 9763 @store.write full_key(key), value
  118. end
  119. 1 def fetch key, &block
  120. 27491 @store.fetch full_key(key), &block
  121. end
  122. 1 def delete key
  123. 1515 @store.delete full_key(key)
  124. end
  125. 1 def exist? key
  126. @store.exist? full_key(key)
  127. end
  128. end
  129. end
  130. end

card/lib/card/cache/prepopulate.rb

45.0% lines covered

20 relevant lines. 9 lines covered and 11 lines missed.
    
  1. 1 class Card
  2. 1 class Cache
  3. # pre-populate cache for testing purposes
  4. 1 module Prepopulate
  5. 1 def restore
  6. reset_soft
  7. prepopulate
  8. end
  9. 1 private
  10. 1 def prepopulate?
  11. Cardio.config.prepopulate_cache
  12. end
  13. 1 def prepopulate
  14. return unless prepopulate?
  15. prepopulate_rule_caches
  16. # prepopulate_lexicon_caches
  17. end
  18. 1 def prepopulate_cache variable
  19. @prepopulated ||= {}
  20. value = @prepopulated[variable] ||= yield
  21. Card.cache.soft.write variable, value.clone
  22. end
  23. # def prepopulate_lexicon_caches
  24. # end
  25. 1 def prepopulate_rule_caches
  26. prepopulate_cache("RULES") { Card::Rule.rule_cache }
  27. prepopulate_cache("READRULES") { Card::Rule.read_rule_cache }
  28. prepopulate_cache("PREFERENCES") { Card::Rule.preference_cache }
  29. end
  30. # def prepopulate_card_cache
  31. # prepopulate_cache "ALL_CARDS" do
  32. # Card.find_each do |card|
  33. # Card.write_to_cache card
  34. # end
  35. # true
  36. # end
  37. # end
  38. end
  39. end
  40. end

card/lib/card/cache/temporary.rb

86.36% lines covered

22 relevant lines. 19 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 class Cache
  3. # The {Temporary} cache is intended for a single request, script,
  4. # migration, etc. It allows you to alter a card and then retrieve
  5. # the card with those alterations intact _without_ saving those
  6. # changes to the database.
  7. #
  8. # In practice, it's a set of Cache-like methods for using a
  9. # simple Hash.
  10. #
  11. # Unlike the Persistent cache, the Temporary cache can handle objects with
  12. # singleton classes.
  13. 1 class Temporary
  14. 1 attr_reader :store
  15. 1 def initialize
  16. 4 @store = {}
  17. end
  18. # @param key [String]
  19. 1 def read key
  20. 217636 return unless @store.key? key
  21. 163360 @store[key]
  22. end
  23. # @param key [String]
  24. 1 def write key, value
  25. 58081 @store[key] = value
  26. end
  27. # @param key [String]
  28. 1 def fetch key, &_block
  29. 80724 read(key) || write(key, yield)
  30. end
  31. # @param key [String]
  32. 1 def delete key
  33. 5515 @store.delete key
  34. end
  35. 1 def dump
  36. @store.each do |k, v|
  37. p "#{k} --> #{v.inspect[0..30]}"
  38. end
  39. end
  40. 1 def reset
  41. 2161 @store = {}
  42. end
  43. # @param key [String]
  44. 1 def exist? key
  45. @store.key? key
  46. end
  47. end
  48. end
  49. end

card/lib/card/codename.rb

88.33% lines covered

60 relevant lines. 53 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # {Card}'s names can be changed, and therefore _names_ should not be directly mentioned
  4. # in code, lest a name change break the application.
  5. #
  6. # Instead, a {Card} that needs specific code manipulations should be given a {Codename},
  7. # which will not change even if the card's name does.
  8. #
  9. # An administrator might add to the Company card via the RESTful web API with a url like
  10. #
  11. # /update/CARDNAME?card[codename]=CODENAME
  12. #
  13. # ...or via the api like
  14. #
  15. # Card[CARDNAME].update! codename: CODENAME
  16. #
  17. # Generally speaking, _codenames_ are represented by Symbols.
  18. #
  19. # The {Codename} class provides a fast cache for this slow-changing data.
  20. # Every process maintains a complete cache that is not frequently reset
  21. #
  22. 1 class Codename
  23. 1 class << self
  24. # returns codename for id and id for codename
  25. # @param codename [Integer, Symbol, String, Card::Name]
  26. # @return [Symbol]
  27. 1 def [] codename
  28. 102101 case codename
  29. when Integer
  30. 80200 codehash[codename]
  31. when Symbol, String
  32. codehash.key?(codename.to_sym) ? codename.to_sym : nil
  33. end
  34. end
  35. 1 def id codename
  36. 9640 case codename
  37. when Symbol, String
  38. 9640 codehash[codename.to_sym]
  39. when Integer
  40. codehash.key?(codename) ? codename : nil
  41. end
  42. end
  43. 1 def name codename=nil
  44. 1316 return super() if codename.nil?
  45. 1316 name! codename
  46. rescue Error::CodenameNotFound => _e
  47. yield if block_given?
  48. end
  49. 1 def card codename
  50. 11 if (card_id = id(codename))
  51. 11 Card[card_id]
  52. elsif block_given?
  53. yield
  54. end
  55. end
  56. 1 def exist? codename
  57. 653 id(codename).present?
  58. end
  59. 1 alias_method :exists?, :exist?
  60. # a Hash in which Symbol keys have Integer values and vice versa
  61. # @return [Hash]
  62. 1 def codehash
  63. 89847 @codehash ||= load_codehash
  64. end
  65. # clear cache both locally and in cache
  66. 1 def reset_cache
  67. 6 @codehash = nil
  68. 6 ::Card.cache.delete "CODEHASH"
  69. end
  70. # @param codename [Symbol, String]
  71. # @return [Integer]
  72. 1 def id! codename
  73. 8889 id(codename) || unknown_codename!(codename)
  74. end
  75. # @param codename [Symbol, String]
  76. # @return [Card::Name]
  77. 1 def name! codename
  78. 1316 Card::Name[codename.to_sym]
  79. end
  80. 1 def generate_id_constants
  81. # If a card has the codename _example_, then Card::ExampleID will
  82. # return the id for that card.
  83. 7 codehash.each do |codename, id|
  84. 4130 next unless codename.is_a?(Symbol) && !codename.to_s.match?(/\W/)
  85. 2037 id_constant codename, id
  86. end
  87. end
  88. 1 private
  89. # iterate through every card with a codename
  90. # @yieldparam codename [Symbol]
  91. # @yieldparam id [Integer]
  92. 1 def each_codenamed_card
  93. 7 sql = "select id, codename from cards where codename is not NULL"
  94. 7 ActiveRecord::Base.connection.select_all(sql).each do |row|
  95. 2065 yield row["codename"].to_sym, row["id"].to_i
  96. end
  97. end
  98. # @todo remove duplicate checks here; should be caught upon creation
  99. 1 def check_duplicates codehash, codename, card_id
  100. 2065 return unless codehash.key?(codename) || codehash.key?(card_id)
  101. Rails.logger.debug "dup codename: #{codename}, "\
  102. "ID:#{card_id} (#{codehash[codename]})"
  103. end
  104. # generate Hash for @codehash and put it in the cache
  105. 1 def load_codehash
  106. 7 ::Card.cache.fetch("CODEHASH") do
  107. 7 generate_codehash
  108. end
  109. end
  110. 1 def generate_codehash
  111. 7 hash = {}
  112. 7 each_codenamed_card do |codename, card_id|
  113. 2065 check_duplicates hash, codename, card_id
  114. 2065 hash[codename] = card_id
  115. 2065 hash[card_id] = codename
  116. end
  117. 7 hash
  118. end
  119. 1 def unknown_codename! mark
  120. raise Card::Error::CodenameNotFound, I18n.t(:exception_unknown_codename,
  121. scope: "lib.card.codename",
  122. codename: mark)
  123. end
  124. 1 def id_constant codename, id=nil
  125. 2037 id ||= id! codename
  126. 2328 Card.const_get_or_set(codename.to_s.camelize + "ID") { id }
  127. end
  128. end
  129. 1 generate_id_constants
  130. end
  131. end

card/lib/card/content.rb

86.11% lines covered

72 relevant lines. 62 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # Content objects support the parsing of content strings into arrays that
  4. # contain semantically meaningful "chunks" like nests, links, urls, etc.
  5. #
  6. # Each chunk has an object whose class inherits from {Card::Content::Chunk::Abstract}
  7. #
  8. 1 class Content < SimpleDelegator
  9. 1 extend Clean
  10. 1 extend Truncate
  11. 1 Chunk # trigger autoload
  12. 1 attr_reader :revision, :format, :chunks, :opts
  13. # initialization parses String, detects chunks
  14. # @param content [String]
  15. # @param format_or_card [Card::Format or Card]
  16. # @param opts [Hash]
  17. # @option opts [Symbol] :chunk_list - name of registered list of chunk
  18. # classes to be used in parsing
  19. 1 def initialize content, format_or_card, opts={}
  20. 2894 @format = resolve_format format_or_card
  21. 2894 opts ||= {}
  22. 2894 chunk_list = opts[:chunk_list] || @format.chunk_list
  23. 2894 @chunks = Parser.new(chunk_list, self).parse(content)
  24. 2894 super(@chunks.any? ? @chunks : content)
  25. end
  26. # Content must be associated with a Format object, which in turn must be
  27. # associated with a Card
  28. # @return [Card]
  29. 1 def card
  30. 5394 @format.card
  31. end
  32. # Find all chunks of a given type
  33. # @param chunk_type [Chunk Class]
  34. # @return [Array of Chunk instances]
  35. 1 def find_chunks chunk_type
  36. 524 each_chunk.select { |chunk| chunk.is_a?(chunk_type) }
  37. end
  38. 1 def has_chunk? chunk_type
  39. each_chunk.any { |chunk| chunk.is_a?(chunk_type) }
  40. end
  41. # sends &block to #process_chunk on each Chunk object
  42. 1 def process_chunks &block
  43. 2601 return custom_process_chunks(&block) if block_given?
  44. 2153 each_chunk(&:process_chunk)
  45. end
  46. 1 def custom_process_chunks
  47. 448 each_chunk do |chunk|
  48. 1792 chunk.burn_after_reading yield(chunk)
  49. end
  50. end
  51. 1 def pieces
  52. Array.wrap(__getobj__)
  53. end
  54. 1 def each_chunk
  55. 3141 return enum_for(:each_chunk) unless block_given?
  56. iterator =
  57. 2896 case __getobj__
  58. when Hash then :each_value
  59. 2003 when Array then :each
  60. 893 when String then return # no chunks
  61. else
  62. Rails.logger.warn "unrecognized type for #each_chunk: " \
  63. " #{self.class} #{__getobj__.class}"
  64. return
  65. end
  66. 14261 send(iterator) { |item| yield item if item.is_a?(Chunk::Abstract) }
  67. end
  68. # convert content to String.
  69. # the common cases here are that either
  70. #
  71. # - (a) content is already a String, or
  72. # - (b) it's an Array that needs to be iterated over and converted into a
  73. # a string by running to_s on each item.
  74. 1 def to_s
  75. 2651 case __getobj__
  76. 1837 when Array then map(&:to_s) * ""
  77. 814 when String then __getobj__
  78. when NilClass then "" # raise "Nil Card::Content"
  79. else __getobj__.to_s
  80. end
  81. end
  82. 1 def inspect
  83. "<#{__getobj__.class}:#{card}:#{self}>"
  84. end
  85. 1 def without_nests
  86. without_chunks Chunk::Nest do |content|
  87. yield content
  88. end
  89. end
  90. 1 def without_references
  91. 50 without_chunks Chunk::Nest, Chunk::Link do |content|
  92. 50 yield content
  93. end
  94. end
  95. 1 def without_chunks *chunk_classes
  96. 50 chunk_classes = ::Set.new Array.wrap(chunk_classes)
  97. 50 stash = stash_chunks chunk_classes
  98. 50 processed = yield to_s
  99. 50 unstash_chunks processed, stash
  100. end
  101. 1 private
  102. 1 def stash_chunks chunk_classes
  103. 50 chunks = []
  104. 50 each_chunk do |chunk|
  105. 143 next unless chunk_classes.include? chunk.class
  106. 135 chunk.burn_after_reading "{{#{chunks.size}}}"
  107. 135 chunks << chunk.text
  108. end
  109. 50 chunks
  110. end
  111. 1 def unstash_chunks content, stashed_chunks
  112. 50 Chunk::Nest.gsub content do |nest_content|
  113. 143 number?(nest_content) ? stashed_chunks[nest_content.to_i] : "{{#{nest_content}}}"
  114. end
  115. end
  116. 1 def resolve_format format_or_card
  117. 2894 if format_or_card.is_a?(Card)
  118. 241 Format.new format_or_card, format: nil
  119. else
  120. 2653 format_or_card
  121. end
  122. end
  123. 1 def number? str
  124. 143 true if Float(str)
  125. rescue StandardError
  126. 8 false
  127. end
  128. end
  129. end

card/lib/card/content/chunk.rb

95.65% lines covered

46 relevant lines. 44 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 require "uri/common"
  3. 1 class Card
  4. 1 class Content < SimpleDelegator
  5. # A chunk is a pattern of text that can be protected
  6. # and interrogated by a format. Each Chunk class has a
  7. # +pattern+ that states what sort of text it matches.
  8. # Chunks are initalized by passing in the result of a
  9. # match by its pattern.
  10. #
  11. 1 module Chunk
  12. 1 mattr_accessor :raw_list, :prefix_regexp_by_list,
  13. :prefix_map_by_list, :prefix_map_by_chunkname
  14. 1 @@raw_list = {}
  15. 1 @@prefix_regexp_by_list = {}
  16. 1 @@prefix_map_by_chunkname = {}
  17. 1 @@prefix_map_by_list = Hash.new { |h, k| h[k] = {} }
  18. 1 class << self
  19. 1 def register_class klass, hash
  20. 9 klass.config = hash.merge class: klass
  21. 9 prefix_index = hash[:idx_char] || :default
  22. # ^ this is gross and needs to be moved out.
  23. 9 klassname = klass.name.split("::").last.to_sym
  24. 9 prefix_map_by_chunkname[klassname] = { prefix_index => klass.config }
  25. 9 raw_list.each do |key, list|
  26. 54 next unless list.include? klassname
  27. 15 prefix_map_by_list[key].merge! prefix_map_by_chunkname[klassname]
  28. end
  29. end
  30. 1 def register_list key, list
  31. 6 raw_list[key] = list
  32. 6 prefix_map_by_list[key] =
  33. list.each_with_object({}) do |chunkname, h|
  34. 15 next unless (p_map = prefix_map_by_chunkname[chunkname])
  35. h.merge! p_map
  36. end
  37. end
  38. 1 def find_class_by_prefix prefix, chunk_list_key=:default
  39. 6047 validate_chunk_list_key chunk_list_key
  40. 6047 prefix_map = prefix_map_by_list[chunk_list_key]
  41. 6047 config = prefix_map[prefix[0, 1]] ||
  42. prefix_map[prefix[-1, 1]] ||
  43. prefix_map[:default]
  44. # prefix identified by first character, last character, or default.
  45. # a little ugly...
  46. 6047 config[:class]
  47. end
  48. 1 def prefix_regexp chunk_list_key
  49. 2894 prefix_regexp_by_list[chunk_list_key] ||=
  50. build_prefix_regexp chunk_list_key
  51. end
  52. 1 def build_prefix_regexp chunk_list_key
  53. 5 validate_chunk_list_key chunk_list_key
  54. prefix_res =
  55. 5 raw_list[chunk_list_key].map do |chunkname|
  56. 14 chunk_class = const_get chunkname
  57. 14 chunk_class.config[:prefix_re]
  58. end
  59. 5 /(?:#{ prefix_res * '|' })/m
  60. end
  61. 1 def validate_chunk_list_key chunk_list_key
  62. 6052 unless raw_list.key? chunk_list_key
  63. raise ArgumentError, "invalid chunk list key: #{chunk_list_key}"
  64. end
  65. end
  66. end
  67. # not sure whether this is best place.
  68. # Could really happen almost anywhere
  69. # (even before chunk classes are loaded).
  70. 1 register_list :default, %i[
  71. URI HostURI EmailURI EscapedLiteral Nest Link
  72. ]
  73. 1 register_list :references, %i[EscapedLiteral Nest Link]
  74. 1 register_list :nest_only, [:Nest]
  75. 1 register_list :query, [:QueryReference]
  76. 1 register_list :stub, [:ViewStub]
  77. 1 register_list :references_keep_escaping, %i[KeepEscapedLiteral Nest Link]
  78. end
  79. end
  80. 1 Card::Mod::Loader.load_chunks
  81. end

card/lib/card/content/chunk/abstract.rb

92.86% lines covered

42 relevant lines. 39 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 class Content < SimpleDelegator
  3. # A chunk is a pattern of text that can be protected
  4. # and interrogated by a format. Each Chunk class has a
  5. # +pattern+ that states what sort of text it matches.
  6. # Chunks are initalized by passing in the result of a
  7. # match by its pattern.
  8. #
  9. 1 module Chunk
  10. 1 class Abstract
  11. 1 class_attribute :config
  12. 1 attr_reader :text, :process_chunk
  13. 1 class << self
  14. # if the prefix regex matched check that chunk against the full regex
  15. 1 def full_match content, prefix=nil
  16. 6047 content.match full_re(prefix)
  17. end
  18. 1 def full_re _prefix
  19. 6031 config[:full_re]
  20. end
  21. 1 def context_ok? _content, _chunk_start
  22. 6041 true
  23. end
  24. end
  25. 1 def reference_code
  26. 126 "I"
  27. end
  28. 1 def initialize match, content
  29. 6047 match = self.class.full_match(match) if match.is_a? String
  30. 6047 @text = match[0]
  31. 6047 @processed = nil
  32. 6047 @content = content
  33. 6047 interpret match, content
  34. end
  35. 1 def interpret _match_string, _content
  36. Rails.logger.info "no #interpret method found for chunk class: " \
  37. "#{self.class}"
  38. end
  39. 1 def format
  40. 14646 @content.format
  41. end
  42. 1 def card
  43. 5394 @content.card
  44. end
  45. 1 def to_s
  46. 5758 result
  47. end
  48. 1 def result
  49. 5758 burn_read || @process_chunk || @processed || @text
  50. end
  51. 1 def burn_read
  52. 5758 return unless @burn_read
  53. 1927 tmp = @burn_read
  54. 1927 @burn_read = nil
  55. 1927 tmp
  56. end
  57. # Temporarily overrides the processed nest content for single-use
  58. # After using the nest's result
  59. # (for example via `to_s`) the original result is restored
  60. 1 def burn_after_reading text
  61. 1927 @burn_read = text
  62. end
  63. 1 def inspect
  64. "<##{self.class}##{self}>"
  65. end
  66. 1 def as_json _options={}
  67. burn_read || @process_chunk || @processed ||
  68. "not rendered #{self.class}, #{card&.name}"
  69. end
  70. end
  71. end
  72. end
  73. end

card/lib/card/content/clean.rb

76.0% lines covered

50 relevant lines. 38 lines covered and 12 lines missed.
    
  1. 1 class Card
  2. 1 class Content
  3. # tools for cleaning content, especially for restricing unwanted HTML
  4. 1 module Clean
  5. 1 allowed_tags = {}
  6. %w[
  7. 1 br i b pre cite caption strong em ins sup sub del ol hr ul li p
  8. div h1 h2 h3 h4 h5 h6 span table tr td th tbody thead tfoot
  9. 32 ].each { |tag| allowed_tags[tag] = [] }
  10. # allowed attributes
  11. 1 allowed_tags.merge!(
  12. "a" => %w[href title target],
  13. "img" => %w[src alt title],
  14. "code" => ["lang"],
  15. "blockquote" => ["cite"]
  16. )
  17. 1 if Cardio.config.allow_inline_styles
  18. 1 allowed_tags["table"] += %w[cellpadding align border cellspacing data-mce-style]
  19. 1 allowed_tags["td"] += %w[scope data-mce-style]
  20. 1 allowed_tags["th"] += %w[scope data-mce-style]
  21. end
  22. 1 allowed_tags.each_key do |k|
  23. 36 allowed_tags[k] << "class"
  24. 36 allowed_tags[k] << "style" if Cardio.config.allow_inline_styles
  25. 36 allowed_tags[k]
  26. end
  27. 1 ALLOWED_TAGS = allowed_tags.freeze
  28. 1 ATTR_VALUE_RE = [/(?<=^')[^']+(?=')/, /(?<=^")[^"]+(?=")/, /\S+/].freeze
  29. 1 def clean! string, tags=ALLOWED_TAGS
  30. 207 cleaned = clean_tags string, tags
  31. 207 cleaned = clean_spaces cleaned if Cardio.config.space_last_in_multispace
  32. 207 cleaned
  33. end
  34. 1 private
  35. ## Method that cleans the String of HTML tags
  36. ## and attributes outside of the allowed list.
  37. 1 def clean_tags string, ok_tags
  38. # $LAST_MATCH_INFO is nil if string is a SafeBuffer
  39. 207 string.to_str.gsub(%r{<(/*)(\w+)([^>]*)>}) do |_raw|
  40. 100 clean_tag $LAST_MATCH_INFO, ok_tags
  41. end.gsub(/<\!--.*?-->/, "")
  42. end
  43. 1 def clean_spaces string
  44. 207 string.gsub(/(?:^|\b) ((?:&nbsp;)+)/, '\1 ')
  45. end
  46. 1 def clean_tag match, ok_tags
  47. 100 tag = match[2].downcase
  48. 100 return " " unless (ok_attrs = ok_tags[tag])
  49. 100 "<#{match[1]}#{html_attribs tag, match[3], ok_attrs}>"
  50. end
  51. 1 def html_attribs tag, raw_attr, ok_attrs
  52. 100 ok_attrs.each_with_object([tag]) do |ok_attr, pcs|
  53. 200 q, rest_value = process_attribute ok_attr, raw_attr
  54. 200 pcs << "#{ok_attr}=#{q}#{rest_value}#{q}" unless rest_value.blank?
  55. end * " "
  56. end
  57. 1 def process_attribute attrib, all_attributes
  58. 200 return ['"', nil] unless all_attributes =~ /\b#{attrib}\s*=\s*(?=(.))/i
  59. q = '"'
  60. rest_value = $'
  61. if (idx = %w[' "].index Regexp.last_match(1))
  62. q = Regexp.last_match(1)
  63. end
  64. reg_exp = ATTR_VALUE_RE[idx || 2]
  65. rest_value = process_attribute_match rest_value, reg_exp, attrib
  66. [q, rest_value]
  67. end
  68. # NOTE allows classes beginning with "w-" (deprecated)
  69. 1 def process_attribute_match rest_value, reg_exp, attrib
  70. return rest_value unless (match = rest_value.match reg_exp)
  71. rest_value = match[0]
  72. if attrib == "class"
  73. rest_value.split(/\s+/).select { |s| s =~ /^w-/i }.join(" ")
  74. else
  75. rest_value
  76. end
  77. end
  78. end
  79. end
  80. end

card/lib/card/content/diff.rb

78.79% lines covered

33 relevant lines. 26 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Content
  4. 1 class Diff
  5. 1 class << self
  6. 1 def complete a, b, opts={}
  7. Card::Content::Diff.new(a, b, opts).complete
  8. end
  9. 1 def summary a, b, opts={}
  10. Card::Content::Diff.new(a, b, opts).summary
  11. end
  12. 1 def render_added_chunk text
  13. 8 "<ins class='diffins diff-added'>#{text}</ins>"
  14. end
  15. 1 def render_deleted_chunk text, _count=true
  16. 12 "<del class='diffdel diff-deleted'>#{text}</del>"
  17. end
  18. end
  19. 1 attr_reader :result
  20. 1 delegate :summary, :complete, :summary_omits_content?, to: :result
  21. # diff options
  22. # :format => :html|:text|:pointer|:raw
  23. # :html = maintain html structure, but compare only content
  24. # :text = remove all html tags; compare plain text
  25. # :pointer = remove all double square brackets
  26. # :raw = escape html tags and compare everything
  27. #
  28. # summary: {length: <number> , joint: <string> }
  29. 1 def initialize old_version, new_version, opts={}
  30. 2 @result = Result.new opts[:summary]
  31. 2 return unless new_version
  32. 2 lcs_opts = lcs_opts_for_format opts[:diff_format]
  33. 2 LCS.new(lcs_opts).run(old_version, new_version, @result)
  34. end
  35. 1 def red?
  36. @result.dels_cnt > 0
  37. end
  38. 1 def green?
  39. @result.adds_cnt > 0
  40. end
  41. 1 private
  42. 1 def lcs_opts_for_format diff_format
  43. 2 opts = {}
  44. 2 case diff_format
  45. when :html
  46. opts[:exclude] = /^</
  47. when :text
  48. 2 opts[:reject] = /^</
  49. 4 opts[:postprocess] = proc { |word| word.gsub("\n", "<br>") }
  50. when :pointer
  51. opts[:preprocess] = proc { |word| word.gsub("[[", "").gsub("]]", "<br>") }
  52. else # :raw
  53. opts[:preprocess] = proc { |word| CGI.escapeHTML(word) }
  54. end
  55. 2 opts
  56. end
  57. end
  58. end
  59. end

card/lib/card/content/diff/l_c_s.rb

75.51% lines covered

49 relevant lines. 37 lines covered and 12 lines missed.
    
  1. # require "card/content/diff/processor"
  2. 1 class Card
  3. 1 class Content
  4. 1 class Diff
  5. # Use LCS algorithm to create a {Diff::Result}
  6. 1 class LCS
  7. 1 def initialize opts
  8. # regex; remove matches completely from diff
  9. 2 @reject_pattern = opts[:reject]
  10. # regex; put matches back to the result after diff
  11. 2 @exclude_pattern = opts[:exclude]
  12. 2 @preprocess = opts[:preprocess] # block; called with every word
  13. 2 @postprocess = opts[:postprocess] # block; called with complete diff
  14. 2 @splitters = %w(<[^>]+> \[\[[^\]]+\]\] \{\{[^}]+\}\} \s+)
  15. 2 @disjunction_pattern = /^\s/
  16. end
  17. 1 def run old_text, new_text, result
  18. 2 @result = result
  19. 2 compare old_text, new_text
  20. 2 @result.complete = postprocess @result.complete
  21. end
  22. 1 private
  23. 1 def compare old_text, new_text
  24. 2 if old_text
  25. 2 old_words, old_ex = separate_comparables_from_excludees old_text
  26. 2 new_words, new_ex = separate_comparables_from_excludees new_text
  27. 2 processor = Processor.new old_words, new_words, old_ex, new_ex
  28. 2 processor.run @result
  29. else
  30. list = split_and_preprocess(new_text)
  31. list = list.reject { |word| word.match @exclude_pattern } if @exclude_pattern
  32. # CAUTION: postproces and added_chunk changed order
  33. # and no longer postprocess for summary
  34. @result.write_added_chunk list.join
  35. end
  36. end
  37. 1 def separate_comparables_from_excludees text
  38. # return two arrays, one with all words, one with pairs
  39. # (index in word list, html_tag)
  40. 4 list = split_and_preprocess text
  41. 4 if @exclude_pattern
  42. check_exclude_and_disjunction_pattern list
  43. else
  44. 4 [list, []]
  45. end
  46. end
  47. 1 def check_exclude_and_disjunction_pattern list
  48. list.each_with_index.each_with_object([[], []]) do |pair, res|
  49. element, index = pair
  50. if element.match? @disjunction_pattern
  51. res[1] << { chunk_index: index, element: element, type: :disjunction }
  52. elsif element.match? @exclude_pattern
  53. res[1] << { chunk_index: index, element: element, type: :excludee }
  54. else
  55. res[0] << element
  56. end
  57. end
  58. end
  59. 1 def split_and_preprocess text
  60. 4 splitted = split_to_list_of_words(text).select do |s|
  61. 16 !s.empty? && (!@reject_pattern || !s.match(@reject_pattern))
  62. end
  63. 4 @preprocess ? splitted.map { |s| @preprocess.call(s) } : splitted
  64. end
  65. 1 def split_to_list_of_words text
  66. 4 split_regex = /(#{@splitters.join '|'})/
  67. 4 text.split(split_regex)
  68. end
  69. 1 def preprocess text
  70. @preprocess ? @preprocess.call(text) : text
  71. end
  72. 1 def postprocess text
  73. 2 @postprocess ? @postprocess.call(text) : text
  74. end
  75. end
  76. end
  77. end
  78. end

card/lib/card/content/diff/l_c_s/processor.rb

80.65% lines covered

93 relevant lines. 75 lines covered and 18 lines missed.
    
  1. 1 class Card
  2. 1 class Content
  3. 1 class Diff
  4. 1 class LCS
  5. # Compares two lists of chunks and generates a diff
  6. 1 class Processor
  7. 1 def initialize old_words, new_words, old_excludees, new_excludees
  8. 2 @adds = []
  9. 2 @dels = []
  10. 2 @words = { old: old_words, new: new_words }
  11. @excludees =
  12. 2 ExcludeeIterator.old_and_new old_excludees, new_excludees
  13. end
  14. 1 def run result
  15. 2 @result = result
  16. 2 prev_action = nil
  17. 2 ::Diff::LCS.traverse_balanced(@words[:old], @words[:new]) do |word|
  18. 10 process_word word, prev_action
  19. 10 prev_action = word.action
  20. end
  21. 2 write_all
  22. 2 @result
  23. end
  24. 1 def process_word word, prev_action
  25. 10 prev_action ? interpret_action(prev_action, word.action) : write_excludees
  26. 10 process_element word.old_element, word.new_element, word.action
  27. end
  28. 1 def interpret_action prev_actn, word_actn
  29. 8 handle_action?(word_actn, prev_actn) ? handle_action(word_actn) : write_all
  30. end
  31. 1 def handle_action? word_action, prev_action
  32. 8 (prev_action == word_action) ||
  33. 6 (prev_action == "-" && word_action == "!") ||
  34. 6 (prev_action == "!" && word_action == "+")
  35. end
  36. 1 def handle_action action
  37. 2 case action
  38. 2 when "-" then del_old_excludees
  39. when "+" then add_new_excludees
  40. when "!" then
  41. del_old_excludees
  42. add_new_excludees
  43. else
  44. write_excludees
  45. end
  46. end
  47. 1 def write_all
  48. 8 write_dels
  49. 8 write_adds
  50. 8 write_excludees
  51. end
  52. 1 def write_unchanged text
  53. 2 @result.write_unchanged_chunk text
  54. end
  55. 1 def write_dels
  56. 8 return if @dels.empty?
  57. 6 @result.write_deleted_chunk @dels.join
  58. 6 @dels = []
  59. end
  60. 1 def write_adds
  61. 8 return if @adds.empty?
  62. 4 @result.write_added_chunk @adds.join
  63. 4 @adds = []
  64. end
  65. 1 def write_excludees
  66. 20 while (ex = @excludees[:new].next)
  67. @result.write_excluded_chunk ex[:element]
  68. end
  69. end
  70. 1 def del_old_excludees
  71. 2 @excludees[:old].scan_and_record(@dels) do |element|
  72. write_dels
  73. @result.write_excluded_chunk element
  74. end
  75. end
  76. 1 def add_new_excludees
  77. @excludees[:new].scan_and_record(@adds) do |element|
  78. write_adds
  79. @result.complete << element
  80. end
  81. end
  82. 1 def process_element old_element, new_element, action
  83. 10 case action
  84. 4 when "-" then minus old_element
  85. when "+" then plus new_element
  86. when "!"
  87. 4 minus old_element
  88. 4 plus new_element
  89. else
  90. 2 write_unchanged new_element
  91. 2 @excludees[:new].word_step
  92. end
  93. end
  94. 1 def plus new_element
  95. 4 @adds << new_element
  96. 4 @excludees[:new].word_step
  97. end
  98. 1 def minus old_element
  99. 8 @dels << old_element
  100. 8 @excludees[:old].word_step
  101. end
  102. end
  103. # support class for LCS::Processor
  104. 1 class ExcludeeIterator
  105. 1 def self.old_and_new old_excludees, new_excludees
  106. {
  107. 2 old: new(old_excludees),
  108. new: new(new_excludees)
  109. }
  110. end
  111. 1 def initialize list
  112. 4 @list = list
  113. 4 @index = 0
  114. 4 @chunk_index = 0
  115. end
  116. 1 def word_step
  117. 14 @chunk_index += 1
  118. end
  119. 1 def next
  120. 12 if @index < @list.size &&
  121. @list[@index][:chunk_index] == @chunk_index
  122. res = @list[@index]
  123. @index += 1
  124. @chunk_index += 1
  125. res
  126. end
  127. end
  128. 1 def scan_and_record record_array
  129. 4 while (ex = self.next)
  130. if ex[:type] == :disjunction
  131. record_array << ex[:element]
  132. else
  133. yield ex[:element]
  134. end
  135. end
  136. end
  137. end
  138. end
  139. end
  140. end
  141. end

card/lib/card/content/diff/result.rb

69.15% lines covered

94 relevant lines. 65 lines covered and 29 lines missed.
    
  1. 1 class Card
  2. 1 class Content
  3. 1 class Diff
  4. # Result object for Diff processing
  5. 1 class Result
  6. 1 attr_accessor :complete, :summary, :dels_cnt, :adds_cnt
  7. 1 def initialize summary_opts=nil
  8. 2 @dels_cnt = 0
  9. 2 @adds_cnt = 0
  10. 2 @complete = ""
  11. 2 @summary = Summary.new summary_opts
  12. end
  13. 1 def summary
  14. 2 @summary.rendered
  15. end
  16. 1 def summary_omits_content?
  17. 2 @summary.omits_content?
  18. end
  19. 1 def write_added_chunk text
  20. 4 @adds_cnt += 1
  21. 4 @complete << Card::Content::Diff.render_added_chunk(text)
  22. 4 @summary.add text
  23. end
  24. 1 def write_deleted_chunk text
  25. 6 @dels_cnt += 1
  26. 6 @complete << Card::Content::Diff.render_deleted_chunk(text)
  27. 6 @summary.delete text
  28. end
  29. 1 def write_unchanged_chunk text
  30. 2 @complete << text
  31. 2 @summary.omit
  32. end
  33. 1 def write_excluded_chunk text
  34. @complete << text
  35. end
  36. # Summary object for Diff processing
  37. 1 class Summary
  38. 1 def initialize opts
  39. 2 opts ||= {}
  40. 2 @remaining_chars = opts[:length] || 50
  41. 2 @joint = opts[:joint] || "..."
  42. 2 @summary = nil
  43. 2 @chunks = []
  44. 2 @content_omitted = false
  45. end
  46. 1 def rendered
  47. 2 @summary ||=
  48. begin
  49. 2 truncate_overlap
  50. 2 @chunks.map do |chunk|
  51. 12 @content_omitted ||= chunk[:action] == :ellipsis
  52. 12 render_chunk chunk[:action], chunk[:text]
  53. end.join
  54. end
  55. end
  56. 1 def add text
  57. 4 add_chunk text, :added
  58. end
  59. 1 def delete text
  60. 6 add_chunk text, :deleted
  61. end
  62. 1 def omit
  63. 2 if @chunks.empty? || @chunks.last[:action] != :ellipsis
  64. 2 add_chunk @joint, :ellipsis
  65. end
  66. end
  67. 1 def omits_content?
  68. 2 @content_omitted || @remaining_chars < 0
  69. end
  70. 1 private
  71. 1 def add_chunk text, action
  72. 12 if @remaining_chars > 0
  73. 12 @chunks << { action: action, text: text }
  74. 12 @remaining_chars -= text.size
  75. end
  76. end
  77. 1 def render_chunk action, text
  78. 12 case action
  79. when "+", :added
  80. 4 Card::Content::Diff.render_added_chunk text
  81. when "-", :deleted
  82. 6 Card::Content::Diff.render_deleted_chunk text
  83. 2 else text
  84. end
  85. end
  86. 1 def truncate_overlap
  87. 2 return unless @remaining_chars < 0
  88. process_ellipsis
  89. index = @chunks.size - 1
  90. while @remaining_chars < @joint.size && index >= 0
  91. overlap_size = @remaining_chars + @chunks[index][:text].size
  92. break if process_overlap overlap_size, index
  93. index -= 1
  94. end
  95. end
  96. 1 def process_ellipsis
  97. return unless @chunks.last[:action] == :ellipsis
  98. @chunks.pop
  99. @content_omitted = true
  100. @remaining_chars += @joint.size
  101. end
  102. 1 def process_overlap overlap_size, index
  103. if overlap_size == @joint.size
  104. replace_with_joint index
  105. true
  106. elsif overlap_size > @joint.size
  107. cut_with_joint index
  108. true
  109. else
  110. @remaining_chars += @chunks[index][:text].size
  111. @chunks.delete_at(index)
  112. false
  113. end
  114. end
  115. 1 def cut_with_joint index
  116. @chunks[index][:text] =
  117. @chunks[index][:text][0..(@remaining_chars - @joint.size - 1)]
  118. @chunks[index][:text] += @joint
  119. end
  120. 1 def replace_with_joint index
  121. @chunks.pop
  122. if index - 1 >= 0
  123. if @chunks[index - 1][:action] == :added
  124. @chunks << { action: :ellipsis, text: @joint }
  125. elsif @chunks[index - 1][:action] == :deleted
  126. @chunks << { action: :added, text: @joint }
  127. end
  128. end
  129. end
  130. end
  131. end
  132. end
  133. end
  134. end

card/lib/card/content/parser.rb

93.88% lines covered

49 relevant lines. 46 lines covered and 3 lines missed.
    
  1. # require "card/content/chunk"
  2. 1 class Card
  3. 1 class Content
  4. # The Content::Parser breaks content strings into an Array of "chunks",
  5. # each of which may be an instance of a {Chunk} class or a simple String.
  6. 1 class Parser
  7. # @param chunk_list [Symbol] name of registered list of chunk classes
  8. # to be used in parsing
  9. # @see Card::Chunk.register_list
  10. # @param content_object [Card::Content]
  11. 1 def initialize chunk_list, content_object
  12. 2894 @content_object = content_object
  13. 2894 @chunk_list = chunk_list
  14. end
  15. # break content string into an array of chunk objects and strings
  16. # @param content [String]
  17. # @return [Array]
  18. 1 def parse content
  19. 2894 @content = content
  20. 2894 @chunks = []
  21. 2894 return @chunks unless content.is_a? String
  22. 2894 @position = @last_position = 0
  23. 2894 @interval_string = ""
  24. 2894 parse_chunks
  25. 2894 @chunks
  26. end
  27. 1 private
  28. 1 def parse_chunks
  29. 2894 prefix_regexp = Chunk.prefix_regexp @chunk_list
  30. 2894 match_prefices prefix_regexp
  31. 2894 handle_remainder
  32. end
  33. 1 def match_prefices prefix_regexp
  34. 2894 while match_prefix prefix_regexp
  35. 6047 @chunk_class = Chunk.find_class_by_prefix @prefix, @chunk_list
  36. # get the chunk class from the prefix
  37. 6047 content_slice = @content[@position..-1]
  38. 6047 @match, @offset = @chunk_class.full_match content_slice, @prefix
  39. # see whether the full chunk actually matches
  40. # (as opposed to bogus prefix)
  41. 6047 if @match # we have a chunk match
  42. 6047 next if record_chunk
  43. else # no match. look at the next character
  44. @position += 1
  45. end
  46. @interval_string += @content[@chunk_start..@position - 1]
  47. # moving beyond the alleged chunk.
  48. # append failed string to "nonchunk" string
  49. end
  50. end
  51. 1 def match_prefix prefix_regexp
  52. 8941 prefix_match = @content[@position..-1].match(prefix_regexp)
  53. 8941 if prefix_match
  54. 6047 @prefix = prefix_match[0]
  55. # prefix of matched chunk
  56. 6047 @chunk_start = prefix_match.begin(0) + @position
  57. # content index of beginning of chunk
  58. 6047 if prefix_match.begin(0) > 0
  59. # if matched chunk is not beginning of test string
  60. 5003 @interval_string += @content[@position..@chunk_start - 1]
  61. # hold onto the non-chunk part of the string
  62. end
  63. 6047 @position = @chunk_start
  64. # move scanning position up to beginning of chunk
  65. 6047 true
  66. end
  67. end
  68. 1 def record_chunk
  69. 6047 @position += (@match.end(0) - @offset.to_i)
  70. # move scanning position up to end of chunk
  71. 6047 if !@chunk_class.context_ok? @content, @chunk_start
  72. # make sure there aren't contextual reasons for ignoring this chunk
  73. false
  74. else
  75. 6047 @chunks << @interval_string unless @interval_string.empty?
  76. 6047 @interval_string = ""
  77. # add the nonchunk string to the chunk list and
  78. # reset interval string for next go-round
  79. 6047 @chunks << @chunk_class.new(@match, @content_object)
  80. # add the chunk to the chunk list
  81. 6047 @last_position = @position
  82. # note that the end of the chunk was the last place where a
  83. # chunk was found (so far)
  84. 6047 true
  85. end
  86. end
  87. 1 def handle_remainder
  88. 2894 if @chunks.any? && @last_position < @content.size
  89. # handle any leftover nonchunk string at the end of content
  90. 1200 @chunks << @content[@last_position..-1]
  91. end
  92. end
  93. end
  94. end
  95. end

card/lib/card/content/truncate.rb

26.32% lines covered

38 relevant lines. 10 lines covered and 28 lines missed.
    
  1. 1 class Card
  2. 1 class Content
  3. # tools for truncating content
  4. 1 module Truncate
  5. 1 ELLIPSES_HTML = '<span class="closed-content-ellipses">...</span>'.freeze
  6. 1 def smart_truncate input, words=25
  7. return if input.nil?
  8. truncated, wordstring = truncate input, words
  9. # nuke partial tags at end of snippet
  10. wordstring.gsub!(/(<[^\>]+)$/, "")
  11. wordstring = close_tags wordstring
  12. wordstring += ELLIPSES_HTML if truncated
  13. # wordstring += '...' if wordlist.length > l
  14. polish wordstring
  15. end
  16. 1 private
  17. 1 def truncate input, words
  18. wordlist = input.to_s.split
  19. l = words.to_i - 1
  20. l = 0 if l.negative?
  21. truncating = wordlist.length > l
  22. wordstring = truncating ? wordlist[0..l].join(" ") : input.to_s
  23. [truncating, wordstring]
  24. end
  25. 1 def close_tags wordstring
  26. tags = find_tags wordstring
  27. tags.each { |t| wordstring += "</#{t}>" }
  28. wordstring
  29. end
  30. 1 def polish wordstring
  31. wordstring.gsub! %r{<[/]?br[\s/]*>}, " "
  32. # Also a hack -- get rid of <br>'s -- they make line view ugly.
  33. wordstring.gsub! %r{<[/]?p[^>]*>}, " "
  34. ## Also a hack -- get rid of <br>'s -- they make line view ugly.
  35. wordstring
  36. end
  37. 1 def find_tags wordstring
  38. tags = []
  39. # match tags with or without self closing (ie. <foo />)
  40. wordstring.scan(%r{\<([^\>\s/]+)[^\>]*?\>}).each do |t|
  41. tags.unshift(t[0])
  42. end
  43. # match tags with self closing and mark them as closed
  44. wordstring.scan(%r{\<([^\>\s/]+)[^\>]*?/\>}).each do |t|
  45. next unless (x = tags.index(t[0]))
  46. tags.slice!(x)
  47. end
  48. # match close tags
  49. wordstring.scan(%r{\</([^\>\s/]+)[^\>]*?\>}).each do |t|
  50. next unless (x = tags.rindex(t[0]))
  51. tags.slice!(x)
  52. end
  53. tags
  54. end
  55. end
  56. end
  57. end

card/lib/card/director.rb

90.48% lines covered

63 relevant lines. 57 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. # Directs the symphony of a card {Card::Act **act**}.
  3. #
  4. # Each act is divided into {Card::Action **actions**}: one action for each card.
  5. # There are three action types: _create_, _update_, and _delete_.
  6. #
  7. # Each action is divided into three **phases**: _validation_, _storage_, and
  8. # _integration_.
  9. #
  10. # Each phase is divided into three **stages**, as follows:
  11. #
  12. # #### Validation Stages
  13. #
  14. # * __VI__: initialize
  15. # * __VP__: prepare_to_validate
  16. # * __VV__: validate
  17. #
  18. # #### Storage Stages
  19. #
  20. # * __SP__: prepare_to_store
  21. # * __SS__: store
  22. # * __SF__: finalize
  23. #
  24. # #### Integration Stages
  25. #
  26. # * __II__: integrate
  27. # * __IA__: after_integrate
  28. # * __ID__: integrate_with_delay
  29. #
  30. # And each stage can have many {Card::Set::Event::Api **events**}, each of which is
  31. # defined using the {Card::Set::Event::Api Event API}.
  32. #
  33. # The table below gives you an overview events can/should do in each stage:
  34. #
  35. # | Phase: | validation | storage | integration
  36. # | Stage: | VI - VP - VV | SP - SS - SF | II - IA - ID
  37. # |---------------------------| :---: | :---: |:---:
  38. # | **tasks**
  39. # | attach subcard | yes! yes! yes | yes yes yes | yes yes no
  40. # | detach subcard | yes! yes! yes | yes no no! | no!
  41. # | validate | yes yes yes! | no | no
  42. # | insecure change [^1] | yes yes! no | no! | no!
  43. # | secure change [^2] | yes | yes! no! no! | no!
  44. # | abort | yes! | yes | yes
  45. # | add errors | yes! | no! | no!
  46. # | subsave | yes | yes | yes!
  47. # | has id (new card) | no | no no? yes | yes
  48. # | within web request | yes | yes | yes yes no
  49. # | within transaction [^3] | yes | yes | no
  50. # | **values**
  51. # | dirty attributes | yes | yes | yes
  52. # | params | yes | yes | yes
  53. # | success | yes | yes | yes
  54. # | session | yes | yes | yes yes no
  55. #
  56. # #### Understanding the Table
  57. #
  58. # - **yes!** the recommended stage to do that
  59. # - **yes** ok to do it here
  60. # - **no** not recommended; risky but not guaranteed to fail
  61. # - **no!** never do it here. it won't work or will break things
  62. #
  63. # If there is only a single entry in a phase column it counts for all stages
  64. # of that phase
  65. #
  66. # [^1]: 'insecure' means a change that might make the card invalid to save
  67. # [^2]: 'secure' means you're sure that the change won't invalidate the card
  68. # [^3]: If an exception is raised in the validation or storage phase
  69. # everything will rollback. If an integration event fails, db changes
  70. # of the other two phases will remain persistent, and other integration
  71. # events will continue to run.
  72. #
  73. # ## Director, Directors, and Subdirectors
  74. #
  75. # Only one act can be performed at a time in any given Card process. Information about
  76. # that act is managed by _Director class methods_. Every act is associated with a
  77. # single "main" card.
  78. #
  79. # The act, however, may involve many cards/actions. Each action has its own _Director
  80. # instance_ that leads the card through all its stages. When a card action (A1)
  81. # initiates a new action on a different card (A2), a new Director object is initialized.
  82. # The new A2 subdirector's @parent is the director of the A1 card. Conversely, the
  83. # A1 card stores a SubdirectorArray in @subdirectors to give it access to A2's
  84. # Director and any little Director babies to which it gave birth.
  85. #
  86. # Subdirectors follow one of two distinct patterns:
  87. #
  88. # 1. {Card::Subcards **Subcards**}. When a card is altered using the subcards API, the
  89. # director follows a "breadth-first" pattern. For each stage a card runs its
  90. # stage events and then triggers its subcards to run that stage before proceeding
  91. # to the next stage. If a subcard is added in a stage then by the end of that
  92. # stage the director will catch it up to the current stage.
  93. # 2. **Subsaves**. When a card is altered by a direct save (`Card.create(!)`,
  94. # `card.update(!)`, `card.delete(!)`, `card.save(!)`...), then the validation and
  95. # storage phases are executed immediately (depth-first), returning the saved card.
  96. # The integration phase, however, is executed following the same pattern as with
  97. # subcards.
  98. #
  99. # Let's consider a subcard example. Suppose you define the following event on
  100. # self/bar.rb
  101. #
  102. # event :met_a_foo_at_the_bar, :prepare_to_store, on: :update do
  103. # add_subcard "foo"
  104. # end
  105. #
  106. # And then you run `Card[:bar].update!({})`.
  107. #
  108. # When bar reaches the event in its `prepare_to_store` stage, the "foo" subcard will be
  109. # added. After that stage ends, the stages `initialize`, `prepare_to_validate`,
  110. # `validate`, and `prepare_to_store` are executed for foo so that it is now caught
  111. # up with Bar at the `prepare_to_store` stage.
  112. #
  113. # If you have subcards within subcards, stages are executed preorder depth-first.
  114. #
  115. # Eg, assuming:
  116. #
  117. # - A has subcards AA and AB
  118. # - AA has subcard AAA
  119. # - AB has subcard ABA
  120. #
  121. # ...then the order of execution is:
  122. #
  123. # 1. A
  124. # 2. AA
  125. # 3. AAA
  126. # 4. AB
  127. # 5. ABA
  128. #
  129. # A special case can happen in the store stage when a supercard needs a subcard's id
  130. # (for example as left_id or as type_id) and the subcard doesn't have an id yet
  131. # (because it gets created in the same act). In this case the subcard's store stage
  132. # is executed BEFORE the supercard's store stage.
  133. #
  134. # ---
  135. 1 class Director
  136. 1 extend EventDelay
  137. 1 extend ActDirection
  138. 1 include Stages
  139. 1 include Phases
  140. 1 include Run
  141. 1 include Store
  142. 1 attr_accessor :act, :card, :stage, :parent, :subdirectors, :head
  143. 1 attr_reader :running
  144. 1 alias_method :running?, :running
  145. 1 def initialize card, parent
  146. 515 @card = card
  147. 515 @card.director = self
  148. # for read actions there is no validation phase
  149. # so we have to set the action here
  150. 515 @stage = nil
  151. 515 @running = false
  152. 515 @prepared = false
  153. 515 @parent = parent
  154. 515 @subdirectors = SubdirectorArray.initialize_with_subcards(self)
  155. 515 register
  156. end
  157. 1 def main?
  158. 6920 parent.nil?
  159. end
  160. 1 def head?
  161. 4058 @head || main?
  162. end
  163. 1 def register
  164. 515 Director.add self
  165. end
  166. 1 def unregister
  167. Director.delete self
  168. end
  169. 1 def delete
  170. 24 @parent&.subdirectors&.delete self
  171. 24 @card.director = nil
  172. 24 @subdirectors.clear
  173. 24 @stage = nil
  174. 24 @action = nil
  175. end
  176. 1 def appoint card
  177. 98 reset_stage
  178. 98 update_card card
  179. 98 @head = true
  180. end
  181. 1 def abort
  182. 88 @abort = true
  183. end
  184. 1 def need_act
  185. 362 act_director = main_director
  186. 362 raise Card::Error, "act requested without a main director" unless act_director
  187. 362 @act = act_director.act ||= Director.need_act
  188. end
  189. 1 def main_director
  190. 362 return self if main?
  191. 183 Director.act_director || (@parent&.main_director)
  192. end
  193. 1 def to_s level=1
  194. str = @card.name.to_s.clone
  195. if @subdirectors.present?
  196. subs = subdirectors.map { |d| " " * level + d.to_s(level + 1) }.join "\n"
  197. str << "\n#{subs}"
  198. end
  199. str
  200. end
  201. 1 def replace_card card
  202. 1 card.action = @card.action
  203. 1 card.director = self
  204. 1 @card = card
  205. 1 reset_stage
  206. 1 catch_up_to_stage @stage if @stage
  207. end
  208. 1 def update_card card
  209. 98 old_card = @card
  210. 98 @card = card
  211. 98 Director.card_changed old_card
  212. end
  213. end
  214. end

card/lib/card/director/act_direction.rb

91.8% lines covered

61 relevant lines. 56 lines covered and 5 lines missed.
    
  1. 1 class Card
  2. 1 class Director
  3. 1 module ActDirection
  4. 1 attr_accessor :act, :act_card
  5. 1 def act_director
  6. 183 return unless act_card
  7. 183 act_card.director
  8. end
  9. 1 def directors
  10. 2806 @directors ||= {}
  11. end
  12. 1 def run_act card
  13. 535 self.act_card = card
  14. # add new_director(card)
  15. 535 yield
  16. ensure
  17. 535 clear
  18. end
  19. 1 def need_act
  20. 179 self.act ||= Card::Act.create ip_address: Env.ip
  21. end
  22. 1 def clear
  23. 535 self.act_card = nil
  24. 535 self.act = nil
  25. 535 directors.each_pair do |card, _dir|
  26. 491 card.expire
  27. 491 card.director = nil
  28. 491 card.action = nil
  29. 491 card.clear_action_specific_attributes
  30. end
  31. 535 expire
  32. 535 @directors = nil
  33. end
  34. 1 def expire
  35. 642 expirees.each { |expiree| Card.expire expiree }
  36. 628 @expirees = []
  37. end
  38. 1 def expirees
  39. 642 @expirees ||= []
  40. end
  41. 1 def fetch card, parent=nil
  42. 539 return directors[card] if directors[card]
  43. 516 directors.each_key do |dir_card|
  44. 396 return dir_card.director if dir_card.name == card.name && dir_card.director
  45. end
  46. 515 add new_director(card, parent)
  47. end
  48. 1 def include? name
  49. directors.keys.any? { |card| card.key == name.to_name.key }
  50. end
  51. 1 def new_director card, parent
  52. 515 if !parent && act_card && act_card != card && running_act?
  53. act_card.director.subdirectors.add card
  54. else
  55. 515 Director.new card, parent
  56. end
  57. end
  58. 1 def card name
  59. 65 directors.values.find do |dir|
  60. 115 dir.card.name == name
  61. end&.card
  62. end
  63. 1 def add director
  64. # Rails.logger.debug "added: #{director.card.name}".green
  65. 1128 directors[director.card] = director
  66. end
  67. 1 def card_changed old_card
  68. 98 return unless (director = @directors.delete old_card)
  69. 98 add director
  70. end
  71. 1 def delete director
  72. 24 return unless @directors
  73. 24 @directors.delete director.card
  74. 24 director.delete
  75. end
  76. 1 def deep_delete director
  77. 24 director.subdirectors.each do |subdir|
  78. deep_delete subdir
  79. end
  80. 24 delete director
  81. end
  82. 1 def running_act?
  83. act_director&.running?
  84. end
  85. 1 def to_s
  86. act_director.to_s
  87. end
  88. end
  89. end
  90. end

card/lib/card/director/card_methods.rb

100.0% lines covered

23 relevant lines. 23 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Director
  3. 1 module CardMethods
  4. 1 attr_writer :director
  5. 1 delegate :validation_phase, :storage_phase, :integration_phase,
  6. :validation_phase_callback?, :integration_phase_callback?, to: :director
  7. 1 def director
  8. 2188 @director ||= Director.fetch self
  9. end
  10. 1 def prepare_for_phases
  11. 474 reset_patterns
  12. 474 identify_action
  13. 474 include_set_modules
  14. end
  15. 1 def identify_action
  16. @action =
  17. 474 if trash && trash_changed?
  18. 12 :delete
  19. 462 elsif new_card?
  20. 308 :create
  21. else
  22. 154 :update
  23. end
  24. end
  25. 1 def restore_changes_information
  26. # restores changes for integration phase
  27. # (rails cleared them in an after_create/after_update hook which is
  28. # executed before the integration phase)
  29. 88 return unless saved_changes.present?
  30. 79 @mutations_from_database = mutations_before_last_save
  31. end
  32. 1 def clear_action_specific_attributes
  33. 491 self.class.action_specific_attributes.each do |attr|
  34. 8347 instance_variable_set "@#{attr}", nil
  35. end
  36. end
  37. end
  38. end
  39. end

card/lib/card/director/event_delay.rb

100.0% lines covered

19 relevant lines. 19 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Director
  3. # methods for handling delayed events
  4. 1 module EventDelay
  5. # If active jobs (and hence the integrate_with_delay events) don't run
  6. # in a background process then Card::Env.deserialize! decouples the
  7. # controller's params hash and the Card::Env's params hash with the
  8. # effect that params changes in the CardController get lost
  9. # (a crucial example are success params that are processed in
  10. # CardController#soft_redirect)
  11. 1 def contextualize_delayed_event act_id, card, env, auth
  12. 93 return yield unless delaying?
  13. 41 with_env_and_auth env, auth do
  14. 82 with_delay_act(act_id, card) { yield }
  15. end
  16. end
  17. 1 def delaying?
  18. 93 Cardio.config.delaying == true
  19. end
  20. 1 def with_delay_act act_id, card, &block
  21. 41 return yield unless act_id && (self.act = Act.find act_id)
  22. 41 run_job_with_act act, card, &block
  23. end
  24. 1 def run_job_with_act act, card, &block
  25. 41 run_act card do
  26. 41 act_card.director.run_delayed_event act, &block
  27. end
  28. end
  29. 1 def with_env_and_auth env, auth
  30. 41 Card::Auth.with auth do
  31. 41 Card::Env.with env do
  32. 41 yield
  33. end
  34. end
  35. end
  36. end
  37. end
  38. end

card/lib/card/director/phases.rb

100.0% lines covered

32 relevant lines. 32 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 180 before_validation :validation_phase, if: -> { validation_phase_callback? }
  3. 1 around_save :storage_phase
  4. 217 after_commit :integration_phase, if: -> { integration_phase_callback? }
  5. 1 class Director
  6. # Validation, Storage, and Integration phase handling
  7. 1 module Phases
  8. 1 def validation_phase_callback?
  9. 179 !@only_storage_phase && head?
  10. end
  11. 1 def integration_phase_callback?
  12. 216 !@only_storage_phase && main?
  13. end
  14. 1 def prepare_for_phases
  15. 680 @card.prepare_for_phases unless running?
  16. 680 @running = true
  17. 680 @subdirectors.each(&:prepare_for_phases)
  18. end
  19. 1 def validation_phase
  20. 179 run_stage :initialize
  21. 179 run_stage :prepare_to_validate
  22. 169 run_stage :validate
  23. ensure
  24. # @card.expire_pieces if @card.errors.any?
  25. 179 @card.errors.empty?
  26. end
  27. # Unlike other phases, the storage phase takes a block,
  28. # because it is called by an "around" callback
  29. 1 def storage_phase &block
  30. 216 catch_up_to_stage :prepare_to_store
  31. 216 run_stage :store, &block
  32. 216 run_stage :finalize
  33. 216 raise ActiveRecord::RecordInvalid, @card if @card.errors.any?
  34. ensure
  35. 216 @from_trash = nil
  36. end
  37. 1 def integration_phase
  38. 88 return if @abort
  39. 88 @card.restore_changes_information
  40. 88 run_stage :integrate
  41. 88 run_stage :after_integrate
  42. 88 run_stage :integrate_with_delay
  43. ensure
  44. 88 @card.clear_changes_information unless @abort
  45. end
  46. end
  47. end
  48. end

card/lib/card/director/run.rb

93.85% lines covered

65 relevant lines. 61 lines covered and 4 lines missed.
    
  1. 1 class Card
  2. 1 class Director
  3. # methods for running stages
  4. 1 module Run
  5. 1 def catch_up_to_stage next_stage
  6. 1266 return if @delay && before?(:integrate_with_delay, next_stage)
  7. 1266 upto_stage(next_stage) do |stage|
  8. 1552 run_stage stage
  9. end
  10. end
  11. 1 def run_delayed_event act
  12. 41 @running = true
  13. 41 @act = act
  14. 41 @stage = stage_index :integrate_with_delay
  15. 41 yield
  16. 41 run_subcard_stages :integrate_with_delay
  17. end
  18. 1 def delay!
  19. @delay = true
  20. end
  21. 1 def restart
  22. @running = false
  23. @stage = nil
  24. end
  25. 1 private
  26. 1 def upto_stage stage
  27. 1266 @stage ||= -1
  28. 1266 (@stage + 1).upto(stage_index(stage)) do |i|
  29. 1552 yield stage_symbol(i)
  30. end
  31. end
  32. 1 def valid_next_stage? next_stage
  33. 2775 @stage ||= -1
  34. 2775 return false if in_or_after?(next_stage) || ahead_of_parent?(next_stage)
  35. 2647 return false unless valid_card? next_stage
  36. 2647 check_skipped_stage next_stage
  37. 2647 true
  38. end
  39. 1 def valid_card? next_stage
  40. 2647 @card.errors.empty? || in_or_before?(:validate, next_stage)
  41. end
  42. 1 def check_skipped_stage stage
  43. 2647 return unless before? previous_stage_index(stage)
  44. raise Card::Error, "stage #{previous_stage_symbol stage} was " \
  45. "skipped for card #{@card}"
  46. end
  47. 1 def ahead_of_parent? next_stage
  48. 2738 return false if head?
  49. 1683 after? parent.stage, next_stage
  50. end
  51. 1 def run_stage stage, &block
  52. 2775 return true unless valid_next_stage? stage
  53. # puts "#{@card.name}: #{stage} stage".yellow
  54. 2647 prepare_stage_run stage
  55. 2647 execute_stage_run stage, &block
  56. end
  57. 1 def prepare_stage_run stage
  58. 2647 @stage = stage_index stage
  59. 2647 prepare_for_phases if stage == :initialize
  60. end
  61. 1 def execute_stage_run stage, &block
  62. # in the store stage it can be necessary that
  63. # other subcards must be saved before we save this card
  64. 2647 return store(&block) if stage == :store
  65. 2303 run_stage_callbacks stage
  66. 2215 run_subcard_stages stage
  67. 2215 run_final_stage_callbacks stage
  68. end
  69. 1 def run_stage_callbacks stage, callback_postfix=""
  70. 4734 Rails.logger.debug "#{stage}: #{@card.name}"
  71. # we use abort :success in the :store stage for :save_draft
  72. 4734 callbacks = :"#{stage}#{callback_postfix}_stage"
  73. 4734 if in_or_before?(:store, stage) && !main?
  74. 3552 @card.abortable { @card.run_callbacks callbacks }
  75. else
  76. 2958 @card.run_callbacks callbacks
  77. end
  78. end
  79. 1 def run_subcard_stages stage
  80. 2688 each_subcard_director stage do |subdir|
  81. 1141 condition = block_given? ? yield(subdir) : true
  82. 1141 subdir.catch_up_to_stage stage if condition
  83. end
  84. end
  85. 1 def each_subcard_director stage
  86. 2688 subdirectors.each do |subdir|
  87. 1141 yield subdir unless subdir.head? && before?(:integrate, stage)
  88. end
  89. ensure
  90. 2688 @card.handle_subcard_errors
  91. end
  92. 1 def run_final_stage_callbacks stage
  93. 2215 run_stage_callbacks stage, "_final"
  94. end
  95. end
  96. end
  97. end

card/lib/card/director/stages.rb

79.55% lines covered

44 relevant lines. 35 lines covered and 9 lines missed.
    
  1. 1 class Card
  2. 1 class Director
  3. # Methods for intepreting stages of an action
  4. 1 module Stages
  5. 1 STAGES = %i[initialize prepare_to_validate validate
  6. prepare_to_store store finalize integrate
  7. after_integrate integrate_with_delay].freeze
  8. 1 STAGE_INDEX = STAGES.each_with_index.with_object({}) do |(stage, index), hash|
  9. 9 Card.define_callbacks "#{stage}_stage", "#{stage}_final_stage"
  10. 9 hash[stage] = index
  11. end.freeze
  12. 1 def stage_symbol index
  13. 1552 case index
  14. when Symbol
  15. return index if STAGE_INDEX[index]
  16. when Integer
  17. 1552 return STAGES[index] if index < STAGES.size
  18. end
  19. raise Card::Error, "not a valid stage index: #{index}"
  20. end
  21. 1 def stage_index stage
  22. 30407 case stage
  23. when Symbol then
  24. 20654 STAGE_INDEX[stage]
  25. when Integer then
  26. 9753 stage
  27. else
  28. raise Card::Error, "not a valid stage: #{stage}"
  29. end
  30. end
  31. 1 def stage_ok? opts
  32. return false unless stage
  33. test = %i[during before after].find { |t| opts[t] }
  34. test ? send("#{test}?", opts[t]) : true
  35. end
  36. 1 def finished_stage? stage
  37. @stage > stage_index(stage)
  38. end
  39. 1 def reset_stage
  40. 99 @stage = -1
  41. end
  42. 1 private
  43. 1 def previous_stage_index from_stage=nil
  44. 2647 from_stage ||= @stage
  45. 2647 stage_index(from_stage) - 1
  46. end
  47. 1 def previous_stage_symbol from_stage=nil
  48. stage_symbol previous_stage_index(from_stage)
  49. end
  50. 1 def before? *args
  51. 5294 stage_test(*args) { |r, t| r > t }
  52. end
  53. 1 def in_or_before? *args
  54. 9468 stage_test(*args) { |r, t| r >= t }
  55. end
  56. 1 def after? *args
  57. 3366 stage_test(*args) { |r, t| r < t }
  58. end
  59. 1 def in_or_after? *args
  60. 5550 stage_test(*args) { |r, t| r <= t }
  61. end
  62. 1 def during? *args
  63. stage_test(*args) { |r, t| r == t }
  64. end
  65. 1 def stage_test reference_stage, test_stage=nil
  66. 11839 test_stage ||= @stage
  67. 11839 yield stage_index(reference_stage), stage_index(test_stage)
  68. end
  69. end
  70. end
  71. end

card/lib/card/director/store.rb

100.0% lines covered

33 relevant lines. 33 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Director
  3. # Special handling specific to the :store stage
  4. 1 module Store
  5. 1 def after_store &block
  6. 44 @after_store ||= []
  7. 44 @after_store << block
  8. end
  9. 1 protected
  10. 1 def after_store?
  11. 400 @after_store.present?
  12. end
  13. 1 private
  14. # The tricky part here is to preserve the dirty marks on the subcards'
  15. # attributes for the finalize stage.
  16. # To achieve this we can't just call the :store and :finalize callbacks on
  17. # the subcards as we do in the other phases.
  18. # Instead we have to call `save` on the subcards and use the ActiveRecord
  19. # :around_save callback.
  20. 1 def store &save_block
  21. 344 raise Card::Error, "need block to store main card" if main? && !block_given?
  22. # the block is the ActiveRecord block from the around save callback that
  23. # saves the card
  24. 344 if block_given?
  25. 216 run_stage_callbacks :store
  26. 216 store_with_subcards(&save_block)
  27. else
  28. 128 trigger_storage_phase_callback
  29. end
  30. end
  31. 1 def store_with_subcards
  32. 216 store_pre_subcards # the exception! usually does nothing
  33. 216 yield
  34. 216 run_after_store_callbacks if after_store?
  35. 216 store_post_subcards # the typical case
  36. 216 true
  37. ensure
  38. 216 @card.handle_subcard_errors
  39. end
  40. 1 def run_after_store_callbacks
  41. 88 @after_store.each { |block| block.call @card }
  42. end
  43. # If the subcard has an after-store callback, it means the subcard
  44. # must run before the supercard and then call back
  45. 1 def store_pre_subcards
  46. 216 run_subcard_stages :store, &:after_store?
  47. end
  48. 1 def store_post_subcards
  49. 308 run_subcard_stages(:store) { |subdir| !subdir.after_store? }
  50. end
  51. # trigger the storage_phase, skip the other phases
  52. # At this point the :prepare_to_store stage was already executed
  53. # by the parent director. So the storage phase will only run
  54. # the :store stage and the :finalize stage
  55. 1 def trigger_storage_phase_callback
  56. 128 @stage = stage_index :prepare_to_store
  57. 128 @only_storage_phase = true
  58. 128 @card.save! validate: false, as_subcard: true
  59. end
  60. end
  61. end
  62. end

card/lib/card/director/subdirector_array.rb

100.0% lines covered

29 relevant lines. 29 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Director
  3. 1 class SubdirectorArray < Array
  4. 1 def self.initialize_with_subcards parent
  5. 515 dir_array = new(parent)
  6. 515 parent.card.subcards.each_card do |subcard|
  7. 104 dir_array.add subcard
  8. end
  9. 515 dir_array
  10. end
  11. 1 def initialize parent
  12. 515 @parent = parent
  13. 515 super()
  14. end
  15. 1 def add card
  16. 444 card = card.card if card.is_a? Director
  17. 444 existing(card) || fetch_new(card)
  18. end
  19. 1 alias_method :delete_director, :delete
  20. 1 def delete card
  21. 48 if card.is_a? Director
  22. 24 delete_director card
  23. else
  24. 35 delete_if { |dir| dir.card == card }
  25. end
  26. end
  27. 1 private
  28. 1 def existing card
  29. 738 find { |dir| dir.card == card }
  30. end
  31. 1 def fetch_new card
  32. 296 Director.fetch(card, @parent).tap do |dir|
  33. 296 update dir, card unless dir.main?
  34. end
  35. end
  36. 1 def update dir, card
  37. 296 dir.replace_card card if dir.card != card
  38. 296 dir.parent = @parent
  39. 296 self << dir
  40. end
  41. end
  42. end
  43. end

card/lib/card/dirty.rb

100.0% lines covered

34 relevant lines. 34 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. # Special "dirty" handling for significant card fields.
  3. 1 module Dirty
  4. 1 extend ::Card::Dirty::MethodFactory
  5. 1 class << self
  6. 1 def dirty_fields
  7. 3 %i[name db_content trash type_id left_id right_id codename]
  8. end
  9. 1 def dirty_aliases
  10. 3 { type: :type_id, content: :db_content }
  11. end
  12. 1 def dirty_options
  13. 2 dirty_fields + dirty_aliases.keys
  14. end
  15. end
  16. 8 dirty_fields.each { |field| define_dirty_methods field }
  17. 3 dirty_aliases.each { |k, v| alias_method "#{k}_is_changing?", "#{v}_is_changing?" }
  18. 1 def attribute_before_act attr
  19. 3945 if saved_change_to_attribute? attr
  20. 350 attribute_before_last_save attr
  21. 3595 elsif will_save_change_to_attribute? attr
  22. 1839 mutations_from_database.changed_values[attr]
  23. 1756 elsif not_in_callback?
  24. 54 attribute_was attr
  25. else
  26. 1702 _read_attribute attr
  27. end
  28. end
  29. 1 def not_in_callback?
  30. # or in integrate_with_delay stage
  31. 10993 mutations_before_last_save.equal?(mutations_from_database)
  32. end
  33. 1 def attribute_is_changing? attr
  34. 9237 if not_in_callback?
  35. 109 attribute_changed? attr
  36. else
  37. 9128 saved_change_to_attribute?(attr) ||
  38. will_save_change_to_attribute?(attr)
  39. end
  40. end
  41. end
  42. # Even special-er handling for dirty cardnames
  43. 1 module DirtyNames
  44. 1 def name_is_changing?
  45. 2959 super || left_id_is_changing? || right_id_is_changing?
  46. end
  47. # def name_before_last_save
  48. # super || dirty_name(left_id_before_last_save, right_id_before_last_save)
  49. # end
  50. 1 def name_before_act
  51. 1400 super || dirty_name(left_id_before_act, right_id_before_act)
  52. end
  53. 1 def dirty_name left, right
  54. 1187 return unless left.present? && right.present?
  55. 100 Card::Name[left, right]
  56. end
  57. end
  58. end

card/lib/card/dirty/method_factory.rb

100.0% lines covered

8 relevant lines. 8 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Dirty
  3. 1 module MethodFactory
  4. 1 def define_dirty_methods field
  5. 9 define_method "#{field}_before_act" do
  6. 3783 attribute_before_act field
  7. end
  8. 9 define_method "#{field}_is_changing?" do
  9. 9237 attribute_is_changing? field
  10. end
  11. end
  12. end
  13. end
  14. end

card/lib/card/env.rb

83.93% lines covered

56 relevant lines. 47 lines covered and 9 lines missed.
    
  1. 1 class Card
  2. # Card::Env is a module for containing the variable details of the environment
  3. # in which Card operates.
  4. #
  5. # Env can differ for each request; Card.config should not.
  6. 1 module Env
  7. 1 extend LocationHistory
  8. 1 extend RequestAssignments
  9. 1 extend SlotOptions
  10. 1 extend Serialization
  11. 1 class << self
  12. 1 def reset args={}
  13. 445 @env = { main_name: nil }
  14. 445 return self unless (c = args[:controller])
  15. 444 self[:controller] = c
  16. 444 self[:session] = c.request.session
  17. 444 self[:params] = c.params
  18. 444 self[:ip] = c.request.remote_ip
  19. 444 self[:ajax] = assign_ajax(c)
  20. 444 self[:html] = assign_html(c)
  21. 444 self[:host] = assign_host(c)
  22. 444 self[:protocol] = assign_protocol(c)
  23. 444 self
  24. end
  25. 1 def [] key
  26. 46660 @env[key.to_sym]
  27. end
  28. 1 def []= key, value
  29. 4727 @env[key.to_sym] = value
  30. end
  31. 1 def params
  32. 37756 self[:params] ||= {}
  33. end
  34. 1 def with_params hash
  35. old_params = params.clone
  36. params.merge! hash
  37. yield
  38. ensure
  39. self.params = old_params
  40. end
  41. 1 def hash hashish
  42. 789 case hashish
  43. when Hash then hashish.clone
  44. 267 when ActionController::Parameters then hashish.to_unsafe_h
  45. 522 else {}
  46. end
  47. end
  48. 1 def session
  49. 2883 self[:session] ||= {}
  50. end
  51. 1 def reset_session
  52. 6 if session.is_a? Hash
  53. self[:session] = {}
  54. else
  55. 6 self[:controller]&.reset_session
  56. end
  57. end
  58. 1 def success cardname=nil
  59. 663 self[:success] ||= Env::Success.new(cardname, params[:success])
  60. end
  61. 1 def localhost?
  62. self[:host]&.match?(/^localhost/)
  63. end
  64. 1 def ajax?
  65. 2210 self[:ajax]
  66. end
  67. 1 def html?
  68. 255 !self[:controller] || self[:html]
  69. end
  70. 1 private
  71. 1 def method_missing method_id, *args
  72. 179 case args.length
  73. 179 when 0 then self[method_id]
  74. when 1 then self[method_id] = args[0]
  75. else super
  76. end
  77. end
  78. end
  79. end
  80. end
  81. 1 Card::Env.reset

card/lib/card/env/location.rb

72.73% lines covered

22 relevant lines. 16 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. 1 module Env
  3. 1 module Location
  4. # card_path makes a relative path site-absolute (if not already)
  5. # card_url makes it a full url (if not already)
  6. 1 def card_path rel_path
  7. 7930 unless rel_path.is_a? String
  8. Rails.logger.warn "Pass only strings to card_path. "\
  9. "(#{rel_path} = #{rel_path.class})"
  10. end
  11. 7930 if rel_path =~ %r{^(https?\:)?/}
  12. 390 rel_path
  13. else
  14. 7540 "#{Card.config.relative_url_root}/#{rel_path}"
  15. end
  16. end
  17. 1 def card_url rel
  18. 499 rel =~ /^https?\:/ ? rel : "#{protocol_and_host}#{card_path rel}"
  19. end
  20. 1 def protocol_and_host
  21. 461 Card.config.protocol_and_host || "#{Env[:protocol]}#{Env[:host]}"
  22. end
  23. 1 def cardname_from_url url
  24. return unless (cardname = cardname_from_url_regexp)
  25. m = url.match cardname
  26. m ? Card::Name[m[:mark]] : nil
  27. end
  28. 1 private
  29. 1 def cardname_from_url_regexp
  30. return unless Env[:host]
  31. %r{#{Regexp.escape Env[:host]}/(?<mark>[^\?]+)}
  32. end
  33. 1 extend Location # allows calls on Location constant, eg Location.card_url
  34. end
  35. end
  36. end

card/lib/card/env/location_history.rb

96.43% lines covered

28 relevant lines. 27 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Env
  3. # session history helpers: we keep a history stack so that in the case of
  4. # card removal we can crawl back up to the last un-removed location
  5. 1 module LocationHistory
  6. 1 def location_history
  7. 308 session[:history] ||= [Env::Location.card_path("")]
  8. 308 session[:history].shift if session[:history].size > 5
  9. 308 session[:history]
  10. end
  11. 1 def save_location card
  12. 315 return unless save_location?(card)
  13. 95 discard_locations_for card
  14. 95 session[:previous_location] =
  15. Env::Location.card_path card.name.url_key
  16. 95 location_history.push previous_location
  17. end
  18. 1 def save_location? card
  19. 315 !Env.ajax? && Env.html? && card.known? && (card.codename != :signin)
  20. end
  21. 1 def previous_location
  22. 104 return unless location_history
  23. 104 session[:previous_location] ||= location_history.last
  24. end
  25. 1 def discard_locations_for card
  26. # quoting necessary because cards have things like "+*" in the names..
  27. 101 session[:history] = location_history.reject do |loc|
  28. 201 if (url_key = url_key_for_location(loc))
  29. 201 url_key.to_name.key == card.key
  30. end
  31. end.compact
  32. 101 session[:previous_location] = nil
  33. end
  34. 1 def save_interrupted_action uri
  35. session[:interrupted_action] = uri
  36. end
  37. 1 def interrupted_action
  38. 78 session.delete :interrupted_action
  39. end
  40. 1 def url_key_for_location location
  41. 201 %r{/([^/]*$)} =~ location ? Regexp.last_match[1] : nil
  42. end
  43. end
  44. end
  45. end

card/lib/card/env/request_assignments.rb

100.0% lines covered

12 relevant lines. 12 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Env
  3. # environmental variables assigned based on request
  4. 1 module RequestAssignments
  5. 1 private
  6. 1 def assign_ajax c
  7. 444 c.request.xhr? || c.request.params[:simulate_xhr]
  8. end
  9. 1 def assign_html c
  10. 444 [nil, "html"].member?(c.params[:format])
  11. end
  12. 1 def assign_host c
  13. 444 Card.config.override_host || c.request.env["HTTP_HOST"]
  14. end
  15. 1 def assign_protocol c
  16. 444 Card.config.override_protocol || c.request.protocol
  17. end
  18. end
  19. end
  20. end

card/lib/card/env/serialization.rb

100.0% lines covered

12 relevant lines. 12 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Env
  3. # serializing environment (eg for delayed jobs)
  4. 1 module Serialization
  5. 1 SERIALIZABLE_ATTRIBUTES = ::Set.new %i[
  6. main_name params ip ajax html host protocol salt
  7. ]
  8. # @param serialized_env [Hash]
  9. 1 def with serialized_env
  10. 41 tmp_env = serialize if @env
  11. 41 @env ||= {}
  12. 41 @env.update serialized_env
  13. 41 yield
  14. ensure
  15. 41 @env.update tmp_env if tmp_env
  16. end
  17. 1 def serialize
  18. 1556 @env.select { |k, _v| SERIALIZABLE_ATTRIBUTES.include?(k) }
  19. end
  20. end
  21. end
  22. end

card/lib/card/env/slot_options.rb

100.0% lines covered

17 relevant lines. 17 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Env
  3. # slot-related environmental variable handling
  4. 1 module SlotOptions
  5. 1 def slot_opts
  6. # FIXME: upgrade to safe parameters
  7. 647 self[:slot_opts] ||= interpret_slot_options
  8. end
  9. 1 private
  10. 1 def interpret_slot_options
  11. 316 opts = hash params[:slot]
  12. 316 opts.merge! shortcut_slot_opts
  13. 316 opts.deep_symbolize_keys.slice(*Card::View::Options.slot_keys)
  14. end
  15. 1 def shortcut_slot_opts
  16. 316 opts = {}
  17. 316 opts[:size] = params[:size].to_sym if params[:size]
  18. 316 opts[:items] = { view: params[:item].to_sym } if slot_items_shortcut?
  19. 316 opts
  20. end
  21. 1 def slot_items_shortcut?
  22. 316 params[:item].present? && !params.dig(:slot, :items, :view).present?
  23. end
  24. end
  25. end
  26. end

card/lib/card/env/success.rb

84.78% lines covered

92 relevant lines. 78 lines covered and 14 lines missed.
    
  1. 1 class Card
  2. 1 module Env
  3. # Success objects
  4. 1 class Success
  5. 1 include Card::Env::Location
  6. 1 attr_accessor :redirect, :name, :name_context, :reload
  7. 1 attr_writer :params, :card
  8. 1 attr_reader :id
  9. 1 def initialize name_context=nil, success_args=nil
  10. 387 @name_context = name_context
  11. 387 @new_args = {}
  12. 387 @params = OpenStruct.new
  13. 387 self << normalize_success_args(success_args)
  14. end
  15. 1 def in_context name_context
  16. 157 self.name_context = name_context
  17. 157 self
  18. end
  19. 1 def normalize_success_args success_args
  20. 387 case success_args
  21. when nil
  22. 335 self.mark = "_self"
  23. 335 {}
  24. when ActionController::Parameters
  25. 1 success_args.to_unsafe_h
  26. else
  27. 51 success_args
  28. end
  29. end
  30. 1 def << value
  31. 398 if value.is_a? Hash
  32. 346 apply value
  33. else
  34. 52 self.target = value
  35. end
  36. end
  37. 1 def reload?
  38. 157 @reload.to_s == "true"
  39. end
  40. # TODO: refactor to use cardish
  41. 1 def mark= value
  42. 392 case value
  43. when Integer then @id = value
  44. 383 when String then @name = value
  45. 9 when Card then @card = value
  46. else
  47. self.target = value
  48. end
  49. end
  50. # @deprecated
  51. 1 def id= id
  52. # for backwards compatibility use mark here.
  53. # id was often used for the card name
  54. 1 self.mark = id
  55. end
  56. 1 def type= type
  57. 9 @new_args[:type] = type
  58. end
  59. 1 def type_id= type_id
  60. @new_args[:type_id] = type_id.to_i
  61. end
  62. 1 def content= content
  63. @new_args[:content] = content
  64. end
  65. 1 def target= value
  66. 62 @id = @name = @card = nil
  67. 62 @target = process_target value
  68. end
  69. 1 def process_target value
  70. 113 case value
  71. 1 when "" then ""
  72. 4 when "*previous", :previous then :previous
  73. 1 when %r{^(http|/)} then value
  74. when /^REDIRECT:\s*(.+)/
  75. 51 @redirect = true
  76. 51 process_target Regexp.last_match(1)
  77. 56 else self.mark = value
  78. end
  79. end
  80. 1 def apply hash
  81. 346 hash.each_pair do |key, value|
  82. 39 self[key] = value
  83. end
  84. end
  85. 1 def card name_context=@name_context
  86. 191 if @card
  87. 18 @card
  88. 173 elsif @id
  89. Card.fetch @id
  90. 173 elsif @name
  91. 160 Card.fetch @name.to_name.absolute(name_context), new: @new_args
  92. end
  93. end
  94. 1 def target name_context=@name_context
  95. 191 card(name_context) ||
  96. 13 (@target == :previous ? Card::Env.previous_location : @target) ||
  97. Card.fetch(name_context)
  98. end
  99. 1 def []= key, value
  100. 39 if respond_to? "#{key}="
  101. 19 send "#{key}=", value
  102. else
  103. 20 @params.send "#{key}=", value
  104. end
  105. end
  106. 1 def [] key
  107. if respond_to? key.to_sym
  108. send key.to_sym
  109. else
  110. @params.send key.to_sym
  111. end
  112. end
  113. 1 def flash message=nil
  114. 300 @params[:flash] ||= []
  115. 300 @params[:flash] << message if message
  116. 300 @params[:flash]
  117. end
  118. 1 def params
  119. 151 @params.marshal_dump
  120. end
  121. 1 def raw_params
  122. @params
  123. end
  124. 1 def to_url name_context=@name_context
  125. 129 case (target = target(name_context))
  126. when Card
  127. 123 target.format.path params
  128. else
  129. 6 target
  130. end
  131. end
  132. 1 def method_missing method, *args
  133. case method
  134. when /^(\w+)=$/
  135. self[Regexp.last_match(1).to_sym] = args[0]
  136. when /^(\w+)$/
  137. self[Regexp.last_match(1).to_sym]
  138. else
  139. super
  140. end
  141. end
  142. 1 def session
  143. Card::Env.session
  144. end
  145. end
  146. end
  147. end

card/lib/card/error.rb

81.18% lines covered

85 relevant lines. 69 lines covered and 16 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # exceptions and errors.
  4. # (arguably most of these should be Card::Exception)
  5. 1 class Error < StandardError
  6. 1 cattr_accessor :current
  7. 1 class_attribute :status_code, :view
  8. 1 attr_writer :backtrace
  9. 1 self.view = :errors
  10. 1 self.status_code = 422
  11. 1 attr_accessor :card
  12. 1 def initialize message=nil
  13. 3 if message.is_a? Card
  14. self.card = message
  15. message = message_from_card
  16. end
  17. 3 super message
  18. end
  19. 1 def message_from_card
  20. I18n.t :exception_for_card,
  21. scope: %i[lib card error], cardname: card.name, message: card_message_text
  22. end
  23. 1 def backtrace
  24. 6 @backtrace || super
  25. end
  26. 1 def report
  27. 3 Rails.logger.info "exception = #{self.class}: #{message}"
  28. 3 Rails.logger.debug backtrace.join("\n")
  29. end
  30. 1 def card_message_text
  31. card.errors.first&.message
  32. end
  33. # error attributable to code (as opposed to card configuration)
  34. 1 class ServerError < Error
  35. 1 def self.view
  36. debugger_on? ? :debug_server_error : :server_error
  37. end
  38. 1 def self.status_code
  39. # Errors with status code 900 are displayed as modal instead of inside
  40. # the "card-notice" div``
  41. debugger_on? ? 900 : 500
  42. end
  43. 1 def self.debugger_on?
  44. Card::Codename[:debugger] && Card[:debugger]&.content =~ /on/
  45. end
  46. 1 def report
  47. super
  48. card&.notable_exception_raised
  49. end
  50. end
  51. # error whose message can be shown to any user
  52. 1 class UserError < Error
  53. 1 cattr_accessor :user_error_classes
  54. 1 self.user_error_classes = [self,
  55. ActionController::BadRequest,
  56. ActionController::MissingFile,
  57. ActiveRecord::RecordNotFound,
  58. ActiveRecord::RecordInvalid]
  59. end
  60. # error in CQL query
  61. 1 class BadQuery < UserError
  62. end
  63. 1 class BadAddress < UserError
  64. 1 self.status_code = 404
  65. 1 self.view = :bad_address
  66. end
  67. # card not found
  68. 1 class NotFound < UserError
  69. 1 self.status_code = 404
  70. 1 self.view = :not_found
  71. end
  72. 1 class CodenameNotFound < NotFound
  73. end
  74. # two editors altering the same card at once
  75. 1 class EditConflict < UserError
  76. 1 self.status_code = 409
  77. 1 self.view = :conflict
  78. end
  79. # permission errors
  80. 1 class PermissionDenied < UserError
  81. 1 self.status_code = 403
  82. 1 self.view = :denial
  83. 1 def card_message_text
  84. card.errors[:permission_denied]
  85. end
  86. end
  87. # exception class for aborting card actions
  88. 1 class Abort < StandardError
  89. 1 attr_reader :status
  90. 1 def report
  91. Rails.logger.debug "aborting: #{message}"
  92. end
  93. 1 def initialize status, msg=""
  94. 88 @status = status
  95. 88 super msg
  96. end
  97. end
  98. # associating views with exceptions
  99. 1 class << self
  100. 1 KEY_MAP = { permission_denied: PermissionDenied,
  101. conflict: EditConflict }.freeze
  102. 1 def report exception, card
  103. 3 e = cardify_exception exception, card
  104. 3 self.current = e
  105. 3 e.report
  106. 3 e
  107. end
  108. 1 def cardify_exception exception, card
  109. card_exception =
  110. 3 if exception.is_a? Card::Error
  111. exception
  112. else
  113. 3 card_error_class(exception, card).new exception.message
  114. end
  115. 3 card_exception.card ||= card
  116. 3 card_exception.backtrace ||= exception.backtrace
  117. 3 add_card_errors card, card_exception if card.errors.empty?
  118. 3 card_exception
  119. end
  120. 1 def add_card_errors card, exception
  121. label = exception.class.to_s.split("::").last
  122. card.errors.add label, exception.message
  123. end
  124. 1 def card_error_class exception, card
  125. # "simple" error messages are visible to end users and are generally not
  126. # treated as software bugs (though they may be "shark" bugs)
  127. 3 case exception
  128. when ActiveRecord::RecordInvalid
  129. 3 invalid_card_error_class card
  130. when ActiveRecord::RecordNotFound, ActionController::MissingFile
  131. Card::Error::NotFound
  132. else
  133. Card::Error::ServerError
  134. end
  135. end
  136. 1 def invalid_card_error_class card
  137. 3 KEY_MAP.each do |key, klass|
  138. 6 return klass if card.errors.key? key
  139. end
  140. 1 Card::Error
  141. end
  142. end
  143. end
  144. end

card/lib/card/format.rb

85.71% lines covered

49 relevant lines. 42 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # Card::Format and its subclasses ({Card::Format::HtmlFormat},
  4. # {Card::Format::JsonFormat}, {Card::Format::XmlFormat}, etc)
  5. # are responsible for defining and rendering _views_.
  6. #
  7. # However, monkeys (those who code in the Card/Decko framework) rarely write code
  8. # directly in these classes. Instead they organize their code using {Card::Mod mods}.
  9. #
  10. # {Card::Mod} explains how to set up a mod.
  11. # {Card::Set::Format} explains how to use this and other format classes within a mod.
  12. # {Card::Set::Format::AbstractFormat} introduces the view API, which is organized with
  13. # these format classes.
  14. #
  15. 1 class Format
  16. 1 extend ActiveSupport::Autoload
  17. 1 extend Registration
  18. 1 include Card::Env::Location
  19. 1 include Nesting
  20. 1 include Render
  21. 1 include Wrapper
  22. 1 include ContextNames
  23. 1 include Content
  24. 1 include Error
  25. 1 include MethodDelegation
  26. 1 cattr_accessor :registered, :aliases
  27. 1 self.registered = []
  28. 1 self.aliases = {}
  29. 1 attr_reader :card, :parent, :main_opts, :modal_opts
  30. 1 attr_accessor :form, :error_status, :rendered
  31. 1 def self.view_caching?
  32. true
  33. end
  34. 1 def initialize card, opts={}
  35. 7007 @card = card
  36. 7007 require_card_to_initialize!
  37. 20163 opts.each { |key, value| instance_variable_set "@#{key}", value }
  38. 7007 include_set_format_modules
  39. end
  40. 1 def require_card_to_initialize!
  41. 7007 return if @card
  42. msg = I18n.t :exception_init_without_card, scope: "lib.card.format"
  43. raise Card::Error, msg
  44. end
  45. 1 def include_set_format_modules
  46. 7007 self.class.format_ancestry.reverse_each do |klass|
  47. 14456 card.set_format_modules(klass).each do |m|
  48. 25869 singleton_class.send :include, m
  49. end
  50. end
  51. end
  52. 1 def page controller, view, slot_opts
  53. 315 @controller = controller
  54. 315 context_names # loads names and removes #name_context from slot_opts
  55. 315 @card.run_callbacks :show_page do
  56. 315 show view, slot_opts
  57. end
  58. end
  59. 1 def params
  60. 3656 Env.params
  61. end
  62. 1 def controller
  63. 425 @controller || Env[:controller] ||= CardController.new
  64. end
  65. 1 def session
  66. Env.session
  67. end
  68. 1 def mime_type
  69. "text/plain"
  70. end
  71. 1 def escape_literal literal
  72. literal
  73. end
  74. 1 def to_sym
  75. Card::Format.format_sym self
  76. end
  77. end
  78. end

card/lib/card/format/content.rb

92.86% lines covered

42 relevant lines. 39 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 module Content
  4. 1 def process_content override_content=nil, content_opts=nil, &block
  5. 2601 content_obj = content_object override_content , content_opts
  6. 2601 content_obj.process_chunks(&block)
  7. 2601 content_obj.to_s
  8. end
  9. # Preserves the syntax in all nests. The content is yielded with placeholders
  10. # for all nests. After executing the given block the original nests are put back in.
  11. # Placeholders are numbers in double curly brackets like {{2}}.
  12. 1 def safe_process_content override_content=nil, content_opts=nil, &block
  13. content_obj =
  14. 50 content_object override_content, chunk_list: :references_keep_escaping
  15. 50 result = content_obj.without_references(&block)
  16. 50 process_content result, content_opts
  17. end
  18. # nested by another card's content
  19. # (as opposed to a direct API nest)
  20. 1 def content_nest opts={}
  21. 3646 return opts[:comment] if opts.key? :comment # commented nest
  22. 3646 nest_name = opts[:nest_name]
  23. 3646 return main_nest(opts) if main_nest?(nest_name) && @nest_mode != :template
  24. 3422 nest nest_name, opts
  25. end
  26. 1 def format_date date, include_time=true
  27. # using DateTime because Time doesn't support %e on some platforms
  28. if include_time
  29. # .strftime('%B %e, %Y %H:%M:%S')
  30. I18n.localize(DateTime.new(date.year, date.mon, date.day,
  31. date.hour, date.min, date.sec),
  32. format: :card_date_seconds)
  33. else
  34. # .strftime('%B %e, %Y')
  35. I18n.localize(DateTime.new(date.year, date.mon, date.day),
  36. format: :card_date_only)
  37. end
  38. end
  39. 1 def add_class options, klass
  40. 8526 return if klass.blank?
  41. 7224 options[:class] = css_classes options[:class], klass
  42. end
  43. 1 alias_method :append_class, :add_class
  44. 1 def prepend_class options, klass
  45. 41 options[:class] = css_classes klass, options[:class]
  46. end
  47. 1 def css_classes *array
  48. 7272 array.flatten.uniq.compact * " "
  49. end
  50. 1 def id_counter
  51. 2640 return @parent.id_counter if @parent
  52. 719 @id_counter ||= 0
  53. 719 @id_counter += 1
  54. end
  55. 1 def unique_id
  56. 719 "#{card.name.safe_key}-#{id_counter}"
  57. end
  58. 1 def output *content
  59. 18528 content = yield if block_given?
  60. 18528 Array.wrap(content).flatten.compact.join "\n"
  61. end
  62. 1 private
  63. 1 def content_object content=nil, content_opts=voo&.content_opts
  64. 2651 return content if content.is_a? Card::Content
  65. 2651 content ||= render_raw || ""
  66. 2651 Card::Content.new content, self, content_opts
  67. end
  68. end
  69. end
  70. end

card/lib/card/format/context_names.rb

89.66% lines covered

29 relevant lines. 26 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. # Contextual names make titles less noisy by not rendering redundant name parts
  4. #
  5. # For example, in the context of "Ball", "Ball+size" is rendered as just "+size"
  6. 1 module ContextNames
  7. 1 def naming name=nil
  8. 248 result = yield
  9. 248 add_name_context name
  10. 248 result
  11. end
  12. # TODO: stop this lazy loading
  13. # the combo of lazy loading + format ancestry navigation + caching is dangerous
  14. # Long term, it would probably be smarter to handle this in the voo.
  15. 1 def context_names
  16. 4876 @context_names ||= initial_context_names
  17. end
  18. 1 def initial_context_names
  19. 3952 @initial_context_names ||= relevant_context_names do
  20. 2885 parent ? parent.context_names : context_names_from_params
  21. end
  22. end
  23. 1 def relevant_context_names
  24. 2885 part_keys = @card.name.part_names.map(&:key)
  25. 4046 yield.select { |n| part_keys.include? n.key }
  26. end
  27. # "slot[name_context]" param is a string; @context_names is an array
  28. 1 def context_names_from_params
  29. 332 return [] unless (name_list = Card::Env.slot_opts.delete(:name_context))
  30. name_list.to_s.split(",").map(&:to_name)
  31. end
  32. 1 def context_names_to_params
  33. return if context_names.empty?
  34. context_names.join(",")
  35. end
  36. 1 def add_name_context name=nil
  37. 322 name ||= card.name
  38. 322 @context_names = (context_names + name.to_name.part_names).uniq
  39. end
  40. 1 def title_in_context title=nil
  41. 1686 keep_safe = title&.html_safe?
  42. 1686 title = title ? title.to_name.absolute_name(card.name) : card.name
  43. 1686 newtitle = title.from(*context_names)
  44. 1686 keep_safe ? newtitle.html_safe : newtitle
  45. end
  46. end
  47. end
  48. end

card/lib/card/format/error.rb

52.17% lines covered

46 relevant lines. 24 lines covered and 22 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 module Error
  4. 1 def ok? task
  5. 7368 task = :create if task == :update && card.new_card?
  6. 7368 card.ok? task
  7. end
  8. 1 def anyone_can? task
  9. return false unless task.is_a? Symbol
  10. @anyone_can ||= {}
  11. @anyone_can[task] = card.anyone_can? task if @anyone_can[task].nil?
  12. @anyone_can[task]
  13. end
  14. 1 def view_for_unknown _view
  15. 25 if main?
  16. root.error_status = 404
  17. :not_found
  18. else
  19. 25 :unknown
  20. end
  21. end
  22. 1 def view_for_denial view, task
  23. 527 @denied_task = task
  24. 527 root.error_status = 403 if focal? && voo.root?
  25. 527 view_setting(:denial, view) || :denial
  26. end
  27. 1 def monitor_depth
  28. 21848 raise Card::Error::UserError, tr(:too_deep) if depth >= Card.config.max_depth
  29. 21848 yield
  30. end
  31. 1 def rescue_view e, view
  32. method = loud_error? ? :loud_error : :quiet_error
  33. send method, e, view
  34. end
  35. 1 def error_cardname _exception
  36. if card&.name.present?
  37. safe_name
  38. else
  39. I18n.t :no_cardname, scope: %i[lib card format error]
  40. end
  41. end
  42. 1 def loud_error?
  43. focal? || Card.config.raise_all_rendering_errors
  44. end
  45. 1 def loud_error e, view
  46. log_error e if focal? && voo.root?
  47. card.errors.add "#{view} view", rendering_error(e, view) if card.errors.empty?
  48. raise e
  49. end
  50. 1 def quiet_error e, view
  51. # TODO: unify with Card::Error#report
  52. log_error e
  53. rendering_error e, view
  54. end
  55. 1 def log_error e
  56. Rails.logger.info e.message
  57. Rails.logger.debug e.backtrace.join("\n")
  58. end
  59. 1 def rendering_error exception, view
  60. if exception.is_a? Card::Error::UserError
  61. exception.message
  62. else
  63. tr :error_rendering, scope: %i[lib card format error],
  64. cardname: error_cardname(exception), view: view
  65. end
  66. end
  67. end
  68. end
  69. end

card/lib/card/format/method_delegation.rb

100.0% lines covered

33 relevant lines. 33 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 module MethodDelegation
  4. RENDER_METHOD_RE =
  5. 1 /^
  6. (?<underscore>_)? # leading underscore to skip permission check
  7. render
  8. (?:_(?<view>\w+))? # view name
  9. (?<bang>!)? # trailing bang to skip optional check
  10. $/x
  11. 1 def api_render match, opts
  12. # view can be part of method name or first argument
  13. 22880 view = match[:view] || opts.shift
  14. 22880 render! view, render_args(match[:underscore], match[:bang], opts)
  15. end
  16. 1 def action_view
  17. 193993 @action_view ||= root? ? new_action_view : root.action_view
  18. end
  19. 1 private
  20. 1 def api_render? method
  21. 162312 method.match RENDER_METHOD_RE
  22. end
  23. 1 def respond_to_missing? method, _include_private=false
  24. 90643 api_render?(method) || action_view?(method)
  25. end
  26. 1 def action_view? method
  27. 90621 action_view.respond_to? method
  28. end
  29. # TODO: make it so we fall back to super if action_view can't handle method.
  30. # It's not as easy as `elsif api_render? method`, because respond_to gives
  31. # false for many methods action view can actually handle, like `h`
  32. 1 def method_missing method, *opts, &proc
  33. 71669 if (match = api_render? method)
  34. 22880 api_render match, opts
  35. else
  36. 66718 delegate_to_action_view(method, opts, proc) { yield }
  37. end
  38. end
  39. 1 def render_args underscore, bang, opts
  40. # opts is a list; args is a hash. we're using various inputs to build the hash
  41. 22880 (opts[0] ? opts.shift.clone : {}).tap do |args|
  42. 22880 args[:optional] = (opts.shift || args[:optional] || :show) unless bang
  43. 22880 args[:skip_perms] = true if underscore
  44. end
  45. end
  46. # TODO: review this. it's quite old, and there might be a better way to do this now.
  47. 1 def new_action_view
  48. 395 c = controller
  49. 395 lookup_context = ActionView::LookupContext.new c.class.view_paths
  50. 395 ActionView::Base.new(lookup_context, { _routes: c._routes }, c).tap do |t|
  51. 395 t.extend c.class._helpers
  52. end
  53. end
  54. 1 def delegate_to_action_view method, opts, proc
  55. 66718 proc = proc { |*a| raw yield(*a) } if proc
  56. 48789 response = action_view.send method, *opts, &proc
  57. 48789 response.is_a?(String) ? action_view.raw(response) : response
  58. end
  59. end
  60. end
  61. end

card/lib/card/format/nest.rb

100.0% lines covered

39 relevant lines. 39 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. # processing nests
  4. 1 class Nest
  5. 1 include Fetch
  6. 1 attr_accessor :format, :card, :view, :view_opts, :format_opts
  7. 1 def initialize format, cardish, view_opts={}, format_opts={}
  8. 6140 @format = format
  9. 6140 @view_opts = view_opts
  10. 6140 @format_opts = format_opts.clone
  11. 6140 @override = @format_opts.delete(:override) != false
  12. 6140 @card ||= fetch_card cardish
  13. # note: fetch_card can alter view and view_opts[:nest_name]
  14. end
  15. 1 def prepare
  16. 6140 prepare_view_and_opts!
  17. 6140 subformat = prepare_subformat
  18. 6140 @view = subformat.modal_nest_view @view if @override
  19. 6140 yield subformat, view
  20. end
  21. 1 private
  22. 1 def prepare_view_and_opts!
  23. 6140 view_opts[:nest_name] ||= card.name
  24. 6140 @view ||= prepare_view
  25. # TODO: handle in closed / edit view definitions
  26. 6140 view_opts[:home_view] ||= %i[closed edit].member?(view) ? :open : view
  27. end
  28. 1 def prepare_view
  29. 6140 view = view_opts[:view] || format.implicit_nest_view
  30. # TODO: canonicalize view and modal_nest_view handling should be in Card::View,
  31. # not here. (Make sure processing only happens on nests/root views)
  32. 6140 Card::View.normalize view
  33. end
  34. # @return [Format] subformat object
  35. 1 def prepare_subformat
  36. 6140 return format if reuse_format?
  37. 6126 sub = format.subformat card, format_opts
  38. 6126 sub.main! if view_opts[:main]
  39. 6126 sub
  40. end
  41. # sometimes we use the same format (rather than a new subformat)
  42. # when nesting the same card. this reduces overhead and optimizes
  43. # caching
  44. 1 def reuse_format?
  45. 6140 self_nest? && !nest_recursion_risk?
  46. end
  47. 1 def self_nest?
  48. 6140 self_nest = view_opts[:nest_name] =~ /^_(self)?$/ &&
  49. format.context_card == format.card
  50. # self nest in focal format should add depth (to catch recursions) but
  51. # remain focal
  52. 6140 format_opts[:focal] = true if self_nest && format.focal?
  53. 6140 self_nest
  54. end
  55. # don't reuse the format when there is a risk of recursion, because while nest
  56. # recursion is caught, view recursion is not.
  57. # TODO: catch view recursion and remove this. (Should be straightforward within voo)
  58. 1 def nest_recursion_risk?
  59. 14 content_view? || format.voo&.structure
  60. end
  61. 1 def content_view?
  62. # TODO: this should be specified in view definition
  63. 14 %i[
  64. bar expanded_bar core content titled open closed open_content one_line_content
  65. ].member? @view.to_sym
  66. end
  67. end
  68. end
  69. end

card/lib/card/format/nest/fetch.rb

85.37% lines covered

41 relevant lines. 35 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 class Nest
  4. # Fetch card for a nest
  5. 1 module Fetch
  6. 1 private
  7. 1 def fetch_card cardish
  8. 6140 case cardish
  9. 796 when Card then cardish
  10. 448 when Symbol, Integer then Card.fetch cardish
  11. 300 when "_", "_self" then format.context_card
  12. 4596 else new_card cardish
  13. end
  14. rescue Card::Error::CodenameNotFound
  15. not_found_codename cardish
  16. end
  17. 1 def not_found_codename cardish
  18. @view = :not_found
  19. c = Card.new name: Array.wrap(cardish).join(Card::Name.joint).to_s
  20. c.errors.add :codename, not_found_codename_error(cardish)
  21. c
  22. end
  23. 1 def not_found_codename_error codename
  24. ::I18n.t :exception_unknown_codename, codename: codename,
  25. scope: "lib.card.codename"
  26. end
  27. 1 def new_card cardish
  28. 4596 view_opts[:nest_name] = Card::Name[cardish].to_s
  29. 4596 Card.fetch cardish, new: new_card_args
  30. end
  31. 1 def new_card_args
  32. 4596 args = { name: view_opts[:nest_name] }
  33. 4596 args[:type] = view_opts[:type] if view_opts[:type]
  34. 4596 args.merge(new_supercard_args)
  35. .merge(new_main_args)
  36. .merge(new_content_args)
  37. end
  38. 1 def new_supercard_args
  39. # special case. gets absolutized incorrectly. fix in name?
  40. 4596 return {} if view_opts[:nest_name].strip.blank?
  41. 4596 { supercard: format.context_card }
  42. end
  43. 1 def new_main_args
  44. 4596 nest_name = view_opts[:nest_name]
  45. 4596 return {} unless nest_name =~ /main/
  46. 224 { name: nest_name.gsub(/^_main\+/, "+"),
  47. supercard: format.root.card }
  48. end
  49. 1 def new_content_args
  50. 4596 content = content_from_shorthand_param || content_from_subcard_params
  51. 4596 content ? { content: content } : {}
  52. end
  53. 1 def content_from_shorthand_param
  54. # FIXME: this is a lame shorthand; could be another card's key
  55. # should be more robust and managed by Card::Name
  56. 4596 shorthand_param = view_opts[:nest_name].tr "+", "_"
  57. 4596 Env.params[shorthand_param]
  58. end
  59. 1 def content_from_subcard_params
  60. 4596 Env.params.dig "subcards", view_opts[:nest_name], "content"
  61. end
  62. end
  63. end
  64. end
  65. end

card/lib/card/format/nesting.rb

75.68% lines covered

37 relevant lines. 28 lines covered and 9 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. # the core of the nesting api
  4. 1 module Nesting
  5. 1 include Main
  6. 1 include Subformat
  7. 1 include Mode
  8. # @param cardish card mark
  9. # @param view_opts [Hash] {Card::View::Options view options}, passed on to render.
  10. # @param format_opts [Hash] opts will be passed on to subformat
  11. 1 def nest cardish, view_opts={}, format_opts={}
  12. 6140 return "" if nest_invisible?
  13. 6140 nest = Card::Format::Nest.new self, cardish, view_opts, format_opts
  14. 6140 nest.prepare do |subformat, view|
  15. 12280 rendered = count_chars { subformat.render view, view_opts }
  16. 6140 block_given? ? yield(rendered, view) : rendered
  17. end
  18. end
  19. # Shortcut for nesting field cards
  20. # @example
  21. # home = Card['home'].format
  22. # home.nest :self # => nest for '*self'
  23. # home.field_nest :self # => nest for 'Home+*self'
  24. 1 def field_nest field, opts={}
  25. 6 fullname = card.name.field(field) unless field.is_a? Card
  26. 6 opts[:title] ||= Card.fetch_name(field).vary("capitalized")
  27. 6 nest fullname, opts
  28. end
  29. # create a path for a nest with respect to the nest options
  30. 1 def nest_path name, nest_opts={}
  31. path_opts = { slot: nest_opts.clone, mark: name }
  32. path_opts[:view] = path_opts[:slot].delete :view
  33. path path_opts
  34. end
  35. # view used if unspecified in nest.
  36. # frequently overridden in other formats
  37. 1 def default_nest_view
  38. :name
  39. end
  40. 1 def implicit_nest_view
  41. 143 voo_items_view || default_nest_view
  42. end
  43. 1 private
  44. 1 def nest_invisible?
  45. 6140 nest_mode == :compact && @char_count && (@char_count > max_char_count)
  46. end
  47. 1 def count_chars
  48. 6140 result = yield
  49. 6140 return result unless nest_mode == :compact && result
  50. @char_count ||= 0
  51. @char_count += result.length
  52. result
  53. end
  54. 1 def max_depth
  55. Card.config.max_depth
  56. end
  57. 1 def max_char_count
  58. Card.config.max_char_count
  59. end
  60. end
  61. end
  62. end

card/lib/card/format/nesting/main.rb

66.67% lines covered

24 relevant lines. 16 lines covered and 8 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 module Nesting
  4. # Handle the main nest
  5. 1 module Main
  6. 1 def wrap_main
  7. yield # no wrapping in base format
  8. end
  9. 1 def main_nest opts
  10. 224 wrap_main do
  11. 224 main.rendered || main_nest_render(opts)
  12. end
  13. end
  14. 1 def main_nest_render opts={}
  15. with_nest_mode :normal do
  16. if block_given?
  17. block.call
  18. else
  19. nest root.card, opts.merge(main_view: true, main: true)
  20. end
  21. end
  22. end
  23. 1 def main_nest? nest_name
  24. 3646 nest_name == "_main" # && !root.already_mained?
  25. end
  26. 1 def already_mained?
  27. return true if @main || @already_main
  28. @already_main = true
  29. false
  30. end
  31. 1 def main!
  32. 224 @main = true
  33. end
  34. # view=edit&items=closed
  35. 1 def main_nest_options
  36. 258 inherit(:main_opts) || {}
  37. end
  38. end
  39. end
  40. end
  41. end

card/lib/card/format/nesting/mode.rb

81.25% lines covered

32 relevant lines. 26 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 module Nesting
  4. # Nest modes are states that can alter a nest's view
  5. 1 module Mode
  6. # A nest can be rendered in one of four modes: normal, compact, edit, or template.
  7. # In _normal_ mode nests are rendered in the requested view without alteration.
  8. # In _compact_ mode nest rendering is altered to fit within a single line
  9. # In _edit_ mode, a nest's view is replaced (where applicable) with a form
  10. # element to edit content
  11. # In _template_ mode, the view is replaced with a link to a nest editor to edit
  12. # rules and options for that nest
  13. # current nest mode
  14. # @return [Symbol] :normal, :compact, :edit, or :template
  15. 1 def nest_mode
  16. 25924 @nest_mode ||= parent ? parent.nest_mode : :normal
  17. end
  18. # run block with new_mode as nest_mode, then return to prior mode
  19. # @param new_mode [Symbol] :normal, :compact, :edit, or :template
  20. # @return block result
  21. 1 def with_nest_mode new_mode, &block
  22. 2158 if new_mode == @nest_mode
  23. 1257 yield
  24. else
  25. 901 with_altered_nest_mode new_mode, &block
  26. end
  27. end
  28. 1 def with_altered_nest_mode new_mode
  29. 901 old_mode = nest_mode
  30. 901 @nest_mode = new_mode
  31. 901 yield
  32. ensure
  33. 901 @nest_mode = old_mode
  34. end
  35. # view to be rendered in current mode
  36. # @param view [Symbol]
  37. # @return [Symbol ] viewname
  38. 1 def modal_nest_view view
  39. # Note: the subformat always has the same nest_mode as its parent format
  40. 6140 case nest_mode
  41. 214 when :edit then view_in_edit_mode(view)
  42. 21 when :template then :template_nest
  43. when :compact then view_in_compact_mode(view)
  44. 5905 else view
  45. end
  46. end
  47. # Returns the view that the card should use when nested in edit mode
  48. # @param view [Symbol]
  49. # @return [Symbol] viewname
  50. 1 def view_in_edit_mode view
  51. 214 hide_view_in_edit_mode?(view) ? :blank : :edit_in_form
  52. end
  53. # @param view [Symbol]
  54. # @return [True/False]
  55. 1 def hide_view_in_edit_mode? view
  56. 214 view_setting(:perms, view) == :none || # view never edited
  57. card.structure || # not yet nesting structures
  58. card.key.blank? # eg {{_self|type}} on new cards
  59. end
  60. # the view that should be used when nested in compact mode
  61. # @param view [Symbol]
  62. # @return [Symbol] viewname
  63. 1 def view_in_compact_mode view
  64. configured_view_in_compact_mode(view) ||
  65. (card.known? ? :one_line_content : :compact_missing)
  66. end
  67. # the view configured in view definition for use when nested in compact mode
  68. # @param view [Symbol]
  69. # @return [Symbol] viewname
  70. 1 def configured_view_in_compact_mode view
  71. compact_config = view_setting(:compact, view)
  72. return view if compact_config == true
  73. compact_config
  74. end
  75. end
  76. end
  77. end
  78. end

card/lib/card/format/nesting/subformat.rb

81.82% lines covered

33 relevant lines. 27 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 module Nesting
  4. # Formats can have subformats. Lineage can be retraced through "parent" format.
  5. 1 module Subformat
  6. # note: while it is possible to have a subformat of a different class,
  7. # the :format_class value takes precedence over :format.
  8. 1 def subformat subcard, opts={}
  9. 6208 subcard = subformat_card subcard
  10. 6208 opts = opts.merge(parent: self).reverse_merge(format_class: self.class)
  11. 6208 self.class.new subcard, opts
  12. end
  13. 1 def root
  14. 13544 @root ||= parent ? parent.root : self
  15. end
  16. 1 def main
  17. 224 if main?
  18. 224 self
  19. elsif !root?
  20. parent.main
  21. end
  22. end
  23. 1 def depth
  24. 35016 @depth ||= parent ? (parent.depth + 1) : 0
  25. end
  26. 1 def root?
  27. 6603 depth.zero?
  28. end
  29. # is format's card the format of the main card on a page?
  30. 1 def main?
  31. 350 depth.zero?
  32. end
  33. # is format's card the format of the requested card?
  34. # (can be different from main in certain formats, see override in HtmlFormat)
  35. 1 def focal?
  36. @focal ||= depth.zero?
  37. end
  38. 1 def field_subformat field
  39. field = card.name.field(field) unless field.is_a?(Card)
  40. subformat field
  41. end
  42. 1 def inherit variable
  43. 2524 variable = "@#{variable}" unless variable.to_s.start_with? "@"
  44. 2524 instance_variable_get(variable) || parent&.inherit(variable)
  45. end
  46. 1 private
  47. 1 def subformat_card subcard
  48. 6208 return subcard if subcard.is_a? Card
  49. Card.fetch subcard, new: {}
  50. end
  51. end
  52. end
  53. end
  54. end

card/lib/card/format/registration.rb

100.0% lines covered

31 relevant lines. 31 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 module Registration
  4. 1 def register format
  5. 9 registered << format.to_s
  6. end
  7. 1 def new card, opts={}
  8. 7806 if self != Format
  9. 7007 super
  10. else
  11. 799 klass = format_class opts
  12. 799 self == klass ? super : klass.new(card, opts)
  13. end
  14. end
  15. 1 def format_class opts
  16. 799 return opts[:format_class] if opts[:format_class]
  17. 799 format = opts[:format] || :html
  18. 799 class_from_name format_class_name(format)
  19. end
  20. 1 def format_class_name format
  21. 1681 format = format.to_s
  22. 1681 format = "" if format == "base"
  23. 1681 format = aliases[format] if aliases[format]
  24. 1681 "#{format.camelize}Format"
  25. end
  26. 1 def format_sym format
  27. 514 return format if format.is_a? Symbol
  28. 257 match = format.to_s.match(/::(?<format>[^:]+)Format/)
  29. 257 match ? match[:format].underscore.to_sym : :base
  30. end
  31. 1 def class_from_name formatname
  32. 1053 if formatname == "Format"
  33. 51 Card::Format
  34. else
  35. 1002 Card::Format.const_get formatname
  36. end
  37. end
  38. 1 def format_ancestry
  39. 14456 ancestry = [self]
  40. 14456 ancestry += superclass.format_ancestry unless self == Card::Format
  41. 14456 ancestry
  42. end
  43. end
  44. end
  45. end

card/lib/card/format/render.rb

67.53% lines covered

77 relevant lines. 52 lines covered and 25 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. # View rendering methods.
  4. #
  5. 1 module Render
  6. # view=open&layout=simple
  7. 1 def render! view, view_options={}
  8. 23354 voo = View.new self, view, view_options, @voo
  9. 23354 with_voo voo do
  10. 23354 voo.process do |final_view|
  11. 21848 final_render final_view
  12. end
  13. end
  14. rescue StandardError => e
  15. rescue_view e, view
  16. end
  17. 1 def with_voo voo
  18. 23354 old_voo = @voo
  19. 23354 @voo = voo
  20. 23354 yield
  21. ensure
  22. 23354 @voo = old_voo
  23. end
  24. 1 def before_view view
  25. 23354 try "_before_#{view}"
  26. end
  27. 1 def voo
  28. 92385 @voo ||= View.new self, nil, {}
  29. end
  30. 1 def show_view? view, default_viz=:show
  31. 2538 voo.process_visibility # trigger viz processing
  32. 2538 visibility = voo.viz_hash[view] || default_viz
  33. 2538 visibility == :show
  34. end
  35. 1 def final_render view
  36. 21848 current_view(view) do
  37. 21848 with_wrapper do
  38. 21848 method = view_method view
  39. 21848 rendered = final_render_call method
  40. 21848 add_debug_info view, method, rendered
  41. end
  42. end
  43. end
  44. 1 def final_render_call method
  45. 21848 method.call
  46. end
  47. 1 def add_debug_info view, method, rendered
  48. 21848 return rendered unless show_debug_info?
  49. <<-HTML
  50. <view-debug view='#{safe_name}##{view}' src='#{pretty_path method.source_location}' module='#{method.owner}'/>
  51. #{rendered}
  52. HTML
  53. end
  54. 1 def show_debug_info?
  55. 21848 Env.params[:debug] == "view"
  56. end
  57. 1 def pretty_path source_location
  58. source_location.first.gsub(%r{^.+mod\d+-([^/]+)}, '\1: ') + ":" +
  59. source_location.second.to_s
  60. end
  61. # :standard, :always, :never
  62. 1 def view_cache_setting view
  63. voo&.cache || view_setting(:cache, view) || :standard
  64. end
  65. 1 def view_setting setting_name, view
  66. 64090 try Card::Set::Format.view_setting_method_name(view, setting_name)
  67. end
  68. 1 def stub_render cached_content
  69. stub_debugging do
  70. expand_stubs cached_content
  71. end
  72. end
  73. 1 def stub_debugging
  74. result = yield
  75. if Rails.env.development? && result =~ /stub/
  76. Rails.logger.debug "STUB IN RENDERED VIEW: #{card.name}: " \
  77. "#{voo.ok_view}\n#{result}"
  78. end
  79. result
  80. end
  81. 1 def prepare_stub_nest stub_hash
  82. stub_card = Card.fetch_from_cast stub_hash[:cast]
  83. view_opts = stub_hash[:view_opts]
  84. voo.normalize_special_options! view_opts
  85. if stub_card&.key.present? && stub_card.key == card.key
  86. view_opts[:nest_name] ||= "_self"
  87. end
  88. yield stub_card, view_opts
  89. end
  90. 1 def expand_stubs cached_content
  91. return cached_content unless cached_content.is_a? String
  92. conto = Card::Content.new cached_content, self, chunk_list: :stub
  93. conto.process_chunks
  94. if conto.pieces.size == 1
  95. # for stubs in json format this converts a single stub back
  96. # to it's original type (e.g. a hash)
  97. conto.pieces.first.to_s
  98. else
  99. conto.to_s
  100. end
  101. end
  102. 1 def view_method view
  103. 21848 unless supports_view? view
  104. raise Card::Error::UserError, unsupported_view_error_message(view)
  105. end
  106. 21848 method Card::Set::Format.view_method_name(view)
  107. end
  108. 1 def supports_view? view
  109. 21848 respond_to? Card::Set::Format.view_method_name(view)
  110. end
  111. 1 def current_view view
  112. 21848 old_view = @current_view
  113. 21848 @current_view = view
  114. 21848 yield
  115. ensure
  116. 21848 @current_view = old_view
  117. end
  118. 1 def stub_nest stub_hash
  119. prepare_stub_nest(stub_hash) do |stub_card, view_opts|
  120. nest stub_card, view_opts, stub_hash[:format_opts]
  121. end
  122. end
  123. end
  124. end
  125. end

card/lib/card/format/wrapper.rb

87.5% lines covered

24 relevant lines. 21 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 module Wrapper
  4. 1 def with_wrapper
  5. 21848 if voo.layout.present?
  6. 224 voo.wrap ||= []
  7. 224 layout = voo.layout.to_name.key
  8. # don't wrap twice with modals or overlays
  9. # this can happen if the view is wrapped with modal
  10. # and is requested with layout=modal param
  11. 224 voo.wrap.unshift layout unless voo.wrap.include? layout.to_sym
  12. end
  13. 21848 @rendered = yield
  14. 21848 wrap_with_wrapper
  15. end
  16. 1 def wrap_with_wrapper
  17. 21848 return @rendered unless voo.wrap.present?
  18. 852 voo.wrap.reverse.each do |wrapper, opts|
  19. @rendered =
  20. 852 render_with_wrapper(wrapper, opts) ||
  21. render_with_card_layout(wrapper) ||
  22. raise_wrap_error(wrapper)
  23. end
  24. 852 @rendered
  25. end
  26. 1 def render_with_wrapper wrapper, opts
  27. 1480 try("wrap_with_#{wrapper}", opts) { @rendered }
  28. end
  29. 1 def render_with_card_layout mark
  30. 224 return unless Card::Layout.card_layout? mark
  31. 224 Card::Layout::CardLayout.new(mark, self).render
  32. end
  33. 1 def raise_wrap_error wrapper
  34. if wrapper.is_a? String
  35. raise Card::Error::UserError, "unknown layout card: #{wrapper}"
  36. else
  37. raise ArgumentError, "unknown wrapper: #{wrapper}"
  38. end
  39. end
  40. end
  41. end
  42. end

card/lib/card/lexicon.rb

95.24% lines covered

42 relevant lines. 40 lines covered and 2 lines missed.
    
  1. 1 class Card
  2. # Translates names to ids and vice versa via a cached "lex" representation:
  3. # name for simple cards, [left_id, right_id] for compound cards.
  4. #
  5. # Note, unlike Card::Fetch, Card::Lexicon:
  6. # 1. does NOT distinguish between trashed and untrashed cards.
  7. # 2. does NOT respect local name changes
  8. 1 module Lexicon
  9. 1 class << self
  10. # param id [Integer]
  11. # @return [String]
  12. 1 def name id
  13. 57040 return unless id.present?
  14. 57040 name = (lex = id_to_lex id) && lex_to_name(lex)
  15. 57040 (name || "").to_name
  16. end
  17. # param name [String]
  18. # @return [Integer]
  19. 1 def id name
  20. 24316 return unless name.present?
  21. 24216 (lex = name_to_lex name.to_name) && lex_to_id(lex)
  22. end
  23. 1 def cache
  24. 80707 Card::Cache[Lexicon]
  25. end
  26. 1 def add card
  27. 134 lex = card.lex
  28. 134 cache.write card.id.to_s, lex
  29. 134 cache.write cache_key(lex), card.id
  30. end
  31. 1 def update card
  32. add card
  33. cache.delete cache_key(card.old_lex)
  34. end
  35. # def delete card
  36. # cache.delete card.id.to_s
  37. # cache.delete cache_key(card.old_lex)
  38. # end
  39. 1 def lex_to_name lex
  40. 59015 return lex unless lex&.is_a? Array
  41. 40236 lex.map { |side_id| name side_id or return }.join(Card::Name.joint).to_name
  42. end
  43. 1 private
  44. 1 def id_to_lex id
  45. 57040 cache.fetch id.to_s do
  46. 4230 result = Card.where(id: id).pluck(:name, :left_id, :right_id).first
  47. 4230 return unless result
  48. 4230 result[0] || [result[1], result[2]]
  49. end
  50. end
  51. 1 def name_to_lex name
  52. 24216 if name.simple?
  53. 17594 name
  54. 6622 elsif (left_id = id name.left_name) && (right_id = id name.right_name)
  55. 5805 [left_id, right_id]
  56. end
  57. end
  58. 1 def lex_to_id lex
  59. 23399 cache.fetch cache_key(lex) do
  60. 4238 Card.where(lex_query(lex)).pluck(:id).first
  61. end
  62. end
  63. 1 def lex_query lex
  64. 4238 if lex.is_a?(Array)
  65. 2497 { left_id: lex.first, right_id: lex.last }
  66. else
  67. 1741 { key: lex.to_name.key }
  68. end
  69. end
  70. 1 def cache_key lex
  71. 23533 "L-" + (lex.is_a?(Array) ? lex.join("-") : lex.to_name.key)
  72. end
  73. end
  74. end
  75. end

card/lib/card/mailer.rb

100.0% lines covered

18 relevant lines. 18 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 require "open-uri"
  3. 1 class Card
  4. 1 class Mailer < ActionMailer::Base
  5. 1 @@defaults = Card.config.email_defaults || {}
  6. 1 @@defaults.symbolize_keys!
  7. 1 @@defaults[:return_path] ||= @@defaults[:from] if @@defaults[:from]
  8. 1 @@defaults[:charset] ||= "utf-8"
  9. 1 default @@defaults
  10. 1 class << self
  11. 1 def new_mail *args, &block
  12. 21 mail = Mail.new(args, &block)
  13. 21 method = Card::Mailer.delivery_method
  14. 21 mail.delivery_method(method, Card::Mailer.send(:"#{method}_settings"))
  15. 21 mail.perform_deliveries = Card::Mailer.perform_deliveries
  16. 21 mail.raise_delivery_errors = Card::Mailer.raise_delivery_errors
  17. 21 mail
  18. end
  19. 1 def layout message
  20. 20 <<-HTML
  21. <!DOCTYPE html>
  22. <html>
  23. <head>
  24. <meta http-equiv="Content-type" content="text/html;charset=UTF-8"/>
  25. </head>
  26. <body>
  27. #{message}
  28. </body>
  29. </html>
  30. HTML
  31. end
  32. end
  33. end
  34. end

card/lib/card/mark.rb

72.41% lines covered

29 relevant lines. 21 lines covered and 8 lines missed.
    
  1. 1 class Card
  2. # Card::Mark provides class methods for Card to translate all different kind
  3. # of card identifiers to card objects.
  4. 1 module Mark
  5. 1 ID_MARK_RE = /^~(?<id>\d+)$/.freeze
  6. 1 CODENAME_MARK_RE = /^:(?<codename>\w+)$/.freeze
  7. # translates marks (and other inputs) into a Card
  8. #
  9. # @param cardish [Card, Card::Name, String, Symbol, Integer]
  10. # @return Card
  11. 1 def cardish cardish
  12. if cardish.is_a? Card
  13. cardish
  14. else
  15. fetch cardish, new: {}
  16. end
  17. end
  18. # translates various inputs into either an id or a name.
  19. # @param parts [Array<Symbol, Integer, String, Card::Name, Card>] a mark or mark parts
  20. # @return [Integer or Card::Name]
  21. 1 def id_or_name parts
  22. 219438 mark = parts.flatten
  23. 219438 mark = mark.first if mark.size <= 1
  24. 219438 id_from_mark(mark) || name_from_mark(mark)
  25. end
  26. 1 def id_from_mark mark
  27. 219438 case mark
  28. 21188 when Integer then mark
  29. 8777 when Symbol then Card::Codename.id! mark
  30. 156900 when String then id_from_string mark
  31. when Card then mark.id
  32. end
  33. end
  34. # translates string identifiers into an id:
  35. # - string id notation (eg "~75")
  36. # - string codename notation (eg ":options")
  37. #
  38. # @param mark [String]
  39. # @return [Integer or nil]
  40. 1 def id_from_string mark
  41. 660567 case mark
  42. 26 when ID_MARK_RE then Regexp.last_match[:id].to_i
  43. 112 when CODENAME_MARK_RE then Card::Codename.id! Regexp.last_match[:codename]
  44. end
  45. end
  46. 1 def bad_mark mark
  47. case mark
  48. when ID_MARK_RE
  49. raise Card::Error::NotFound, "id doesn't exist: #{Regexp.last_match[:id]}"
  50. when CODENAME_MARK_RE
  51. raise Card::Error::CodenameNotFound,
  52. "codename doesn't exist: #{Regexp.last_match[:codename]}"
  53. else
  54. raise Card::Error, "invalid mark: #{mark}"
  55. end
  56. end
  57. 1 def name_from_mark mark
  58. 189345 Card::Name[mark]
  59. end
  60. end
  61. end

card/lib/card/mod.rb

94.74% lines covered

19 relevant lines. 18 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. # A Card Mod (short for "module" or "modification") is a discrete piece of Decko
  3. # functionality. Mods are how the Decko community develops and shares code.
  4. # If you want to customize a deck in a way that can't be done on the site itself,
  5. # try a mod.
  6. #
  7. # The simplest way to add a mod is to run this command in your deck:
  8. #
  9. # decko generate card:mod MOD_NAME
  10. #
  11. # This will create the following directories:
  12. #
  13. # DECK_NAME/mod/MOD_NAME
  14. # DECK_NAME/mod/MOD_NAME/lib
  15. # DECK_NAME/mod/MOD_NAME/public
  16. # DECK_NAME/mod/MOD_NAME/set
  17. #
  18. # The lib directory contains libraries, of course. And files in the public directory
  19. # are public and served directly.
  20. #
  21. # But in most mods, the focal point is the *set* directory.
  22. #
  23. # ## Set Modules
  24. #
  25. # Set modules define methods for a given set of cards and their format objects.
  26. # They are defined in a mod's _set_ directory. For example, suppose you've created a
  27. # mod that called *biz*, your deck has Company cards, and you want to extend the
  28. # behavior of those cards.
  29. #
  30. # You can add a set module like so:
  31. #
  32. # decko generate set biz type company
  33. #
  34. # This will create the following two files:
  35. #
  36. # mod/biz/set/type/company.rb
  37. # mod/biz/spec/set/type/company.rb
  38. #
  39. # If you would like to break this code into smaller files, you can extend this
  40. # pattern into another directory, eg:
  41. #
  42. # mod/biz/set/type/company/foo.rb
  43. # mod/biz/set/type/company/bar.rb
  44. #
  45. # The general pattern can be expressed as follows:
  46. #
  47. # DECKNAME/mod/MODNAME/set/SET_PATTERN/ANCHOR[/FREENAME].rb
  48. #
  49. # Learn more:
  50. # - {Card} introduces card objects
  51. # - {Card::Set} provides an overview of how set modules work
  52. # - {Card::Set::Format} explains the basics of the format API
  53. # - {Card::Set::Format::AbstractFormat} explains the basics of the view definition API
  54. # - {Card::Set::Event:Api} explains the basics of the event API
  55. #
  56. # ## Other Directories
  57. #
  58. # Other ways your mod can extend Decko functionality include:
  59. # - **format** for creating new formats (think file extensions)
  60. # - **set_pattern** for additional {Card::Set::Pattern set patterns},
  61. # or types of sets.
  62. # - **chunk** provides tools for finding new patterns in card content
  63. # - **file** for fixed initial card content
  64. 1 module Mod
  65. 1 class << self
  66. 1 def load
  67. 1 return if ENV["CARD_MODS"] == "none"
  68. 1 if Card.take
  69. 1 Card::Mod::Loader.load_mods
  70. else
  71. Rails.logger.warn "empty database"
  72. end
  73. end
  74. # @return an array of Rails::Path objects
  75. 1 def dirs
  76. 2160 @dirs ||= Mod::Dirs.new(Card.paths["mod"].existent)
  77. end
  78. 1 def dependencies name, nickname=true
  79. 16 return unless (spec = gem_spec name, nickname)
  80. 8 deps = spec&.dependencies || []
  81. 19 dep_names = deps.map { |dep| dependencies dep.name, false }
  82. 8 (dep_names << spec).flatten.compact.uniq
  83. end
  84. 1 def gem_spec name, nickname=true
  85. 16 name = "card-mod-#{name}" if nickname && !name.match?(/^card-mod/)
  86. 16 spec = Gem::Specification.stubs_for(name)&.first
  87. 16 Cardio.gem_mod_spec?(spec) ? spec : nil
  88. end
  89. end
  90. end
  91. end

card/lib/card/mod/dirs.rb

90.91% lines covered

77 relevant lines. 70 lines covered and 7 lines missed.
    
  1. 1 class Card
  2. 1 class << self
  3. 1 def config
  4. 58926 Cardio.config
  5. end
  6. 1 def paths
  7. 627 Cardio.paths
  8. end
  9. end
  10. 1 module Mod
  11. # Dirs objects are used to manage the load paths for card mods.
  12. # Mods can be loaded as gems and by using directories with mod subdirectories.
  13. #
  14. # 1. Gemfile
  15. # A mod gem needs a metadata attribute with { "card-mod" => "the_mod_name" }
  16. # or the name has to start with "card-mod-".
  17. # Then you can just add it to your Gemfile. Otherwise it won't be recognized as mod.
  18. #
  19. # 2. mod directory
  20. # Give a path to a directory with mods. The mods will be loaded in alphabetical order.
  21. # To change the load order you can add number prefixes to the mod names
  22. # (like "01_this_first") or add a Modfile.
  23. # In the Modfile you list all the mods you want to be loaded from that directory
  24. # in load order with a preceding "mod" command (similar to a Gemfile).
  25. # The mods are expected in subdirectories with the mod names.
  26. #
  27. # Mods in Modfiles are always loaded before mods in the Gemfile.
  28. # If you have to change the order add gem mods to your Modfile using the
  29. # mod_gem command. You can omit the 'card-mod' prefix.
  30. #
  31. # Example for a mod directory:
  32. # # my_mod/Modfile
  33. # mod "twitter"
  34. # gem_mod "logger"
  35. # mod "cache"
  36. #
  37. # # directory structure
  38. # my_mods/
  39. # Modfile
  40. # cache/
  41. # set/
  42. # all/
  43. # my_cache.rb
  44. # twitter/
  45. # set/
  46. # type/
  47. # basic.rb
  48. # set_pattern/
  49. # my_pattern.rb
  50. #
  51. # Dir checks always for gems. You can initialize an Dirs object with an additional
  52. # array of paths to card mod directories.
  53. 1 class Dirs < Array
  54. 1 attr_reader :mods
  55. # @param mod_paths [String, Array<String>] paths to directories that contain mods
  56. 1 def initialize mod_paths=[]
  57. 1 @mods = []
  58. 1 @loaded_gem_mods = ::Set.new
  59. 1 @paths = {}
  60. 1 mod_paths = Array(mod_paths)
  61. 1 mod_paths.each do |mp|
  62. 2 @current_path = mp
  63. 2 load_from_modfile || load_from_dir
  64. end
  65. 1 load_from_gemfile
  66. 1 super()
  67. 1 @mods.each do |mod_name|
  68. 31 self << @paths[mod_name]
  69. end
  70. end
  71. # Add a mod to mod load paths
  72. 1 def add_path mod_name, path=nil
  73. 31 if @mods.include? mod_name
  74. raise Error,
  75. "name conflict: mod with name \"#{mod_name}\" already loaded"
  76. end
  77. 31 @mods << mod_name
  78. 31 path ||= File.join @current_path, mod_name
  79. 31 @paths[mod_name] = path
  80. end
  81. 1 def gem_mod name
  82. 5 deps = Mod.dependencies name
  83. 5 unknown_gem_mod!(name) if deps.blank?
  84. 13 deps.each { |spec| add_gem_mod spec.name, spec.full_gem_path }
  85. end
  86. 1 def unknown_gem_mod! name
  87. raise Error, "Unknown gem \"#{name}\". Make sure it is in your Gemfile."
  88. end
  89. 1 def add_gem_mod mod_name, mod_path
  90. 32 return if @loaded_gem_mods.include?(mod_name)
  91. 24 @loaded_gem_mods << mod_name
  92. 24 add_path mod_name, mod_path
  93. end
  94. 1 alias_method :mod, :add_path
  95. # @param mod_name [String] the name of a mod
  96. # @return the path to mod `mod_name`
  97. 1 def path mod_name
  98. 2153 @paths[mod_name] || @paths["card-mod-#{mod_name}"]
  99. end
  100. # Iterate over each mod directory
  101. # @param type [Symbol] the type of modification like set, set_pattern, or format.
  102. # It is attached as subdirectory.
  103. 1 def each type=nil
  104. 6 super() do |path|
  105. 186 dirname = type ? File.join(path, type.to_s) : path
  106. 186 next unless Dir.exist? dirname
  107. 13 yield dirname
  108. end
  109. end
  110. 1 def each_tmp type
  111. 1 @mods.each do |mod|
  112. 31 path = tmp_dir mod, type
  113. 31 next unless Dir.exist? path
  114. 28 yield path
  115. end
  116. end
  117. 1 def each_with_tmp type=nil
  118. 1 @mods.each do |mod|
  119. 31 dirname = type ? File.join(@paths[mod], type.to_s) : @paths[mod]
  120. 31 next unless Dir.exist? dirname
  121. 28 yield dirname, tmp_dir(mod, type)
  122. end
  123. end
  124. 1 def each_assets_path
  125. @mods.each do |mod|
  126. path = File.join(@paths[mod], "public", "assets")
  127. next unless Dir.exist? path
  128. yield mod, path
  129. end
  130. end
  131. 1 private
  132. 1 def load_from_modfile
  133. 2 modfile_path = File.join @current_path, "Modfile"
  134. 2 return unless File.exist? modfile_path
  135. 1 eval File.read(modfile_path), binding
  136. 1 true
  137. end
  138. 1 def load_from_dir
  139. 1 Dir.entries(@current_path).sort.each do |filename|
  140. 3 next if filename =~ /^\./
  141. add_path filename
  142. end.compact
  143. end
  144. 1 def load_from_gemfile
  145. 1 Cardio.gem_mod_specs.each do |mod_name, mod_spec|
  146. 24 add_gem_mod mod_name, mod_spec.full_gem_path
  147. end
  148. end
  149. 1 def tmp_dir modname, type
  150. 59 index = @mods.index modname
  151. 59 File.join Card.paths["tmp/#{type}"].first,
  152. 59 "mod#{'%03d' % (index + 1)}-#{modname}"
  153. end
  154. end
  155. end
  156. end

card/lib/card/mod/load_strategy.rb

76.92% lines covered

39 relevant lines. 30 lines covered and 9 lines missed.
    
  1. 1 class Card
  2. 1 module Mod
  3. # Shared code for the three different load strategies: Eval, TmpFiles and BindingMagic
  4. 1 class LoadStrategy
  5. 1 def self.klass symbol
  6. case symbol
  7. when :tmp_files then TmpFiles
  8. when :binding_magic then BindingMagic
  9. else Eval
  10. end
  11. end
  12. 1 def initialize mod_dirs, loader
  13. 2 @mod_dirs = mod_dirs
  14. 2 @loader = loader
  15. end
  16. 1 def clean_comments?
  17. false
  18. end
  19. 1 private
  20. 1 def module_type
  21. 2 @loader.class.module_type
  22. end
  23. 1 def module_template
  24. 438 @loader.class.module_class_template
  25. end
  26. 1 def each_file &block
  27. 1 if module_type == :set
  28. each_set_file(&block)
  29. else
  30. 1 each_mod_dir module_type do |base_dir|
  31. 1 each_file_in_dir base_dir, &block
  32. end
  33. end
  34. end
  35. 1 def each_set_file &block
  36. each_mod_dir :set do |base_dir|
  37. @loader.patterns.each do |pattern|
  38. each_file_in_dir base_dir, pattern.to_s, &block
  39. end
  40. end
  41. end
  42. 1 def each_mod_dir module_type
  43. 1 @mod_dirs.each module_type do |base_dir|
  44. 1 yield base_dir
  45. end
  46. end
  47. 1 def each_file_in_dir base_dir, subdir=nil
  48. 1 pattern = File.join(*[base_dir, subdir, "**/*.rb"].compact)
  49. 1 Dir.glob(pattern).sort.each do |abs_path|
  50. 9 rel_path = abs_path.sub("#{base_dir}/", "")
  51. 9 const_parts = parts_from_path rel_path
  52. 9 yield abs_path, const_parts
  53. end
  54. end
  55. 1 def parts_from_path path
  56. # remove file extension and number prefixes
  57. 438 parts = path.gsub(/\.rb/, "").gsub(%r{(?<=\A|/)\d+_}, "").split(File::SEPARATOR)
  58. 438 parts.map(&:camelize)
  59. end
  60. end
  61. end
  62. end

card/lib/card/mod/load_strategy/pattern_tmp_files.rb

100.0% lines covered

17 relevant lines. 17 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Mod
  3. 1 class LoadStrategy
  4. # The {TmpFiles} load strategy version for set pattern modules/
  5. 1 class PatternTmpFiles < TmpFiles
  6. 1 private
  7. 1 def generate_tmp_files
  8. 1 prepare_tmp_dir "tmp/set_pattern"
  9. 1 seq = 100
  10. 1 each_file do |abs_path, const_parts|
  11. 9 pattern = const_parts.first.underscore
  12. 9 to_file = "#{tmp_dir}/#{seq}-#{pattern}.rb"
  13. 9 write_tmp_file abs_path, to_file, const_parts
  14. 9 seq += 1
  15. end
  16. end
  17. 1 def load_tmp_files
  18. 1 Loader.load_dir tmp_dir
  19. end
  20. 1 def tmp_dir
  21. 10 Card.paths["tmp/set_pattern"].first
  22. end
  23. end
  24. end
  25. end
  26. end

card/lib/card/mod/load_strategy/set_tmp_files.rb

100.0% lines covered

19 relevant lines. 19 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Mod
  3. 1 class LoadStrategy
  4. # The {TmpFiles} load strategy version for set modules
  5. 1 class SetTmpFiles < LoadStrategy::TmpFiles
  6. 1 private
  7. 1 def generate_tmp_files
  8. 1 return unless prepare_tmp_dir "tmp/set"
  9. 1 @mod_dirs.each_with_tmp(:set) do |mod_dir, mod_tmp_dir|
  10. 28 Dir.mkdir mod_tmp_dir
  11. 28 Dir.glob("#{mod_dir}/**/*.rb").each do |abs_path|
  12. 429 rel_path = abs_path.sub "#{mod_dir}/", ""
  13. 429 tmp_filename = File.join mod_tmp_dir, rel_path
  14. 429 const_parts = parts_from_path rel_path
  15. # puts "write_tmp_file #{abs_path}, #{tmp_filename}, #{const_parts}"
  16. 429 write_tmp_file abs_path, tmp_filename, const_parts
  17. end
  18. end
  19. end
  20. 1 def load_tmp_files
  21. 1 @mod_dirs.each_tmp(:set) do |set_tmp_dir|
  22. 28 Card::Set::Pattern.loadable_codes.each do |pattern|
  23. 280 pattern_dir = "#{set_tmp_dir}/#{pattern}"
  24. 280 Loader.load_dir "#{pattern_dir}/**" if Dir.exist? pattern_dir
  25. end
  26. end
  27. end
  28. end
  29. end
  30. end
  31. end

card/lib/card/mod/load_strategy/tmp_files.rb

100.0% lines covered

23 relevant lines. 23 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Mod
  3. 1 class LoadStrategy
  4. # LoadStrategy for mod modules. It writes the code to tmp files
  5. # and then loads the tmp files. (deprecated)
  6. 1 class TmpFiles < LoadStrategy
  7. 1 def load_modules
  8. 2 generate_tmp_files if rewrite_tmp_files?
  9. 2 load_tmp_files
  10. end
  11. 1 def clean_comments?
  12. 1305 true
  13. end
  14. 1 private
  15. 1 def prepare_tmp_dir path
  16. 2 return unless rewrite_tmp_files?
  17. 2 p = Card.paths[path]
  18. 2 FileUtils.rm_rf p.first, secure: true if p.existent.first
  19. 2 Dir.mkdir p.first
  20. end
  21. 1 def rewrite_tmp_files?
  22. 4 if defined?(@rewrite)
  23. 2 @rewrite
  24. else
  25. 2 @rewrite = !(Rails.env.production? &&
  26. Card.paths["tmp/set"].existent.first)
  27. end
  28. end
  29. 1 def write_tmp_file from_file, to_file, const_parts
  30. 438 FileUtils.mkdir_p File.dirname(to_file)
  31. 438 mt = module_template.new const_parts, from_file, self
  32. 438 File.write to_file, mt.to_s
  33. end
  34. end
  35. end
  36. end
  37. end

card/lib/card/mod/loader.rb

80.95% lines covered

42 relevant lines. 34 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 module Mod
  4. # Card::Mod::Loader is used to load all part of a mod,
  5. # i.e. initializers, patterns, formats, chunks, layouts and sets
  6. # cards are not accessible at this point
  7. # A Loader object provides tools for generating and loading sets and set patterns,
  8. # each of which are typically written using a Decko DSL.
  9. # The mods are given by a Mod::Dirs object.
  10. # SetLoader can use three different strategies to load the set modules.
  11. 1 class Loader
  12. 1 def initialize load_strategy: nil, mod_dirs: nil
  13. 2 load_strategy ||= Cardio.config.load_strategy
  14. 2 mod_dirs ||= Mod.dirs
  15. 2 klass = load_strategy_class load_strategy
  16. 2 @load_strategy = klass.new mod_dirs, self
  17. end
  18. 1 def load_strategy_class load_strategy
  19. case load_strategy
  20. when :tmp_files then LoadStrategy::TmpFiles
  21. when :binding_magic then LoadStrategy::BindingMagic
  22. else LoadStrategy::Eval
  23. end
  24. end
  25. 1 def load
  26. 2 @load_strategy.load_modules
  27. end
  28. 1 class << self
  29. 1 attr_reader :module_type
  30. 1 def load_mods
  31. 1 load_formats
  32. 1 Card::Mod::Loader::SetPatternLoader.new.load
  33. 1 Card::Mod::Loader::SetLoader.new.load
  34. 1 Card::Set.process_base_modules
  35. 1 load_initializers
  36. end
  37. 1 def reload_sets
  38. Card::Set::Pattern.reset
  39. Card::Set.reset_modules
  40. Card::Mod::Loader::SetPatternLoader.new.load
  41. Card::Mod::Loader::SetLoader.new(
  42. patterns: Card::Set::Pattern.nonbase_loadable_codes
  43. ).load
  44. end
  45. 1 def load_chunks
  46. 1 Mod.dirs.each(:chunk) do |dir|
  47. 1 load_dir dir
  48. end
  49. end
  50. 1 def module_class_template
  51. 438 const_get :Template
  52. end
  53. # private
  54. 1 def load_initializers
  55. 1 Card.config.paths["mod/config/initializers"].existent.sort.each do |initializer|
  56. 2 load initializer
  57. end
  58. end
  59. # {Card::Format}
  60. 1 def load_formats
  61. # cheating on load issues now by putting all inherited-from formats in core mod.
  62. 1 Mod.dirs.each(:format) do |dir|
  63. 3 load_dir dir
  64. end
  65. end
  66. # load all files in directory
  67. # @param dir [String] directory name
  68. 1 def load_dir dir
  69. 92 Dir["#{dir}/*.rb"].sort.each do |file|
  70. # puts Benchmark.measure("from #load_dir: rd: #{file}") {
  71. # require file
  72. # "require" breaks the reloading in development env
  73. 458 load file
  74. # }.format('%n: %t %r')
  75. end
  76. end
  77. end
  78. end
  79. end
  80. end

card/lib/card/mod/loader/set_loader.rb

91.25% lines covered

80 relevant lines. 73 lines covered and 7 lines missed.
    
  1. 1 class Card
  2. 1 module Mod
  3. 1 class Loader
  4. # A SetLoader object loads all set modules for a list of mods.
  5. # The mods are given by a Mod::Dirs object.
  6. # SetLoader can use three different strategies to load the set modules.
  7. 1 class SetLoader < Loader
  8. 1 @module_type = :set
  9. 1 attr_accessor :patterns
  10. 1 def initialize args={}
  11. 1 @patterns = args.delete(:patterns) || Card::Set::Pattern.loadable_codes
  12. 1 super
  13. end
  14. 1 def load_strategy_class load_strategy
  15. 1 case load_strategy
  16. when :tmp_files
  17. 1 LoadStrategy::SetTmpFiles
  18. when :binding_magic
  19. LoadStrategy::SetBindingMagic
  20. else
  21. LoadStrategy::Eval
  22. end
  23. end
  24. 1 def load
  25. 1 super
  26. # Card::Set.process_base_modules
  27. 1 Card::Set.clean_empty_modules
  28. end
  29. 1 class Template < ModuleTemplate
  30. 1 def initialize modules, content_path, strategy
  31. 429 super
  32. 429 @modules.pop if helper_module?
  33. end
  34. 1 def to_const
  35. return Object if simple_load?
  36. @modules.inject(pattern_class) do |const, name_part|
  37. const.const_get_or_set name_part do
  38. Module.new
  39. end
  40. end
  41. end
  42. 1 def processed_content
  43. 429 add_explicit_format_modules if @strategy.clean_comments?
  44. 429 super
  45. end
  46. 1 def add_explicit_format_modules
  47. 429 @content.gsub!(/^ *format +:?(\w+)? *do *$/) do
  48. 277 format_name = $1.blank? ? nil : $1.to_sym
  49. 277 "module #{module_name format_name}; " \
  50. "module_parent.send :register_set_format, "\
  51. "#{format_class format_name}, self; "\
  52. "extend Card::Set::AbstractFormat"
  53. end
  54. end
  55. 1 def module_name format_name
  56. 508 Card::Format.format_class_name format_name
  57. end
  58. 1 def format_class format_name
  59. 277 klass = ["Card::Format"]
  60. 277 klass << module_name(format_name) if format_name
  61. 277 klass.join "::"
  62. end
  63. 1 def helper_module?
  64. 858 if @is_helper_module.nil?
  65. 852 @is_helper_module = @content =~ /\A#!\s?not? set module/
  66. else
  67. 6 @is_helper_module
  68. end
  69. end
  70. # correct line number for error messages
  71. 1 def offset
  72. -5
  73. end
  74. 1 private
  75. 1 def submodule_chain
  76. 951 @modules.map { |m| "module #{m};" }
  77. end
  78. 1 def module_chain
  79. 858 @module_chain ||=
  80. ["class Card", "module Set", "class #{@pattern.camelize}"] + submodule_chain
  81. end
  82. 1 def preamble_bits
  83. 429 capture_last_module
  84. 429 [module_chain.join("; "),
  85. module_comment,
  86. @last_module,
  87. set_extension,
  88. location_method].compact
  89. end
  90. 1 def auto_comment
  91. 429 "# Set: #{label_body(*@modules)}\n#"
  92. end
  93. 1 def label_body *anchors
  94. 429 if @pattern == "Abstract"
  95. 58 "Abstract (#{@modules.join ', '})"
  96. else
  97. 371 pattern_label(*anchors)
  98. end
  99. end
  100. 1 def pattern_label *anchors
  101. 371 anchor_count = pattern_class.anchor_parts_count
  102. 371 label = pattern_class.label(pattern_anchor(*anchors, anchor_count))
  103. 371 remainder = anchors[anchor_count..-1]
  104. 371 label += " (#{remainder.join ', '})" if remainder.any?
  105. 371 label
  106. end
  107. 1 def pattern_anchor *anchors, anchor_count
  108. 371 if anchor_count.zero?
  109. 152 ""
  110. else
  111. 219 anchors[0..anchor_count].join(Card::Name.joint)
  112. end
  113. end
  114. 1 def pattern_class
  115. 742 @pattern_class ||= Card::Set.const_get_or_set(@pattern.camelize) { Class.new }
  116. end
  117. 1 def capture_last_module
  118. 429 module_chain
  119. 429 @last_module = @module_chain.pop
  120. end
  121. 1 def set_extension
  122. 429 "extend Card::Set" unless helper_module?
  123. end
  124. 1 def location_method
  125. 429 %(def self.source_location; "#{@content_path}"; end)
  126. end
  127. 1 def postamble
  128. 429 "end;" * (@modules.size + 3)
  129. end
  130. end
  131. end
  132. end
  133. end
  134. end

card/lib/card/mod/loader/set_pattern_loader.rb

80.0% lines covered

25 relevant lines. 20 lines covered and 5 lines missed.
    
  1. 1 class Card
  2. 1 module Mod
  3. 1 class Loader
  4. 1 class SetPatternLoader < Loader
  5. 1 @module_type = :set_pattern
  6. 1 def load_strategy_class load_strategy
  7. 1 case load_strategy
  8. when :tmp_files
  9. 1 LoadStrategy::PatternTmpFiles
  10. else # :eval
  11. LoadStrategy::Eval
  12. end
  13. end
  14. 1 class Template < ModuleTemplate
  15. 1 def to_const
  16. return Object if simple_load?
  17. Card::Set.const_get_or_set(@pattern.camelize) do
  18. Class.new(Card::Set::Pattern::Base)
  19. end
  20. end
  21. # correct line number for error messages
  22. 1 def offset
  23. -5
  24. end
  25. 1 private
  26. 1 def auto_comment
  27. 9 %(# Set Pattern: #{@pattern.camelize}\n#)
  28. end
  29. 1 def module_chain
  30. 9 "class Card::Set::#{@pattern.camelize} < Card::Set::Pattern::Base"
  31. end
  32. 1 def preamble_bits
  33. 9 [module_comment,
  34. module_chain,
  35. "extend Card::Set::Pattern::Helper",
  36. "cattr_accessor :options",
  37. "class << self"]
  38. end
  39. 1 def postamble
  40. 9 <<-RUBY
  41. end
  42. register "#{@pattern}".underscore.to_sym, (options || {})
  43. end
  44. RUBY
  45. end
  46. end
  47. end
  48. end
  49. end
  50. end

card/lib/card/mod/module_template.rb

97.3% lines covered

37 relevant lines. 36 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Mod
  3. # ModuleTemplate is an abstract class to build ruby modules out of
  4. # deckos dsl for sets and set patterns.
  5. # {Loader::SetLoader::Template} and {Loader::SetPatternLoader::Template} inherit
  6. # from it and adapt the template to their needs.
  7. 1 class ModuleTemplate
  8. 1 def initialize modules, content_path, strategy
  9. 438 modules = Array.wrap modules
  10. 438 @pattern = modules.shift
  11. 438 @modules = modules
  12. 438 @content = ::File.read content_path
  13. 438 @content_path = content_path
  14. 438 @strategy = strategy
  15. end
  16. # Evaluates the module in the top level namespace.
  17. 1 def build
  18. eval to_s, TOPLEVEL_BINDING, @content_path, offset
  19. end
  20. # @return [String] the ruby code to build the modal
  21. 1 def to_s
  22. 438 simple_load? ? @content : processed_content
  23. end
  24. 1 def processed_content
  25. 438 capture_module_comment if @strategy.clean_comments?
  26. 438 module_content
  27. end
  28. # Just run the code of the source.
  29. # Don't use the path to the file as module hierarchy.
  30. 1 def simple_load?
  31. 438 @content =~ /\A#!\s?simple load/
  32. end
  33. 1 private
  34. # find all comment lines at the beginning of a mod file, up to the first
  35. # non-comment line. (These will be inserted before the module declaration,
  36. # so that Yard will interpret them as a module comment.)
  37. 1 def capture_module_comment
  38. 438 content_lines = @content.split "\n"
  39. 438 comment_lines = []
  40. 438 content_lines.each do |line|
  41. 554 comment?(line) ? comment_lines << content_lines.shift : break
  42. end
  43. 438 @content = content_lines.join "\n"
  44. 438 @module_comment = comment_lines.join "\n"
  45. end
  46. 1 def comment? line
  47. 554 line.match?(/^ *\#/)
  48. end
  49. # loader template must implement #preamble_bits
  50. 1 def preamble
  51. 438 preamble_bits.join "\n"
  52. end
  53. 1 def module_comment
  54. 438 return "" unless @strategy.clean_comments?
  55. 438 @module_comment = nil if @module_comment.blank?
  56. 438 [auto_comment, @module_comment].compact.join "\n"
  57. end
  58. 1 def module_content
  59. # for unknown reasons strip_heredoc doesn't work properly
  60. # and with trailing whitespace code like `=begin` fails
  61. 438 <<~RUBY.strip_heredoc
  62. # -*- encoding : utf-8 -*-
  63. #{preamble}
  64. #{@content}
  65. #{postamble}
  66. # ~~ generated from #{@content_path} ~~
  67. RUBY
  68. end
  69. end
  70. end
  71. end

card/lib/card/name.rb

93.18% lines covered

44 relevant lines. 41 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # require "card/env"
  3. 1 require "cardname"
  4. 1 class Card
  5. # The Cardname class provides generalized of Card naming patterns
  6. # (compound names, key-based variants, etc)
  7. #
  8. # Card::Name adds support for deeper card integration
  9. 1 class Name < Cardname
  10. 1 include FieldsAndTraits
  11. 1 include NameVariants
  12. 1 class << self
  13. 1 def [] *cardish
  14. 217618 cardish = cardish.first if cardish.size <= 1
  15. 217618 case cardish
  16. 1107 when Card then cardish.name
  17. 5832 when Symbol, Integer then Card.fetch_name(cardish)
  18. 3925 when Array then smart_compose cardish
  19. 206754 when String, NilClass then new cardish
  20. else
  21. raise ArgumentError, "#{cardish.class} not supported as name identifier"
  22. end
  23. end
  24. 1 def session
  25. 103 Card::Auth.current.name # also_yuck
  26. end
  27. 1 def params
  28. 224 Card::Env.params
  29. end
  30. 1 def new str, validated_parts=nil
  31. 575795 return compose str if str.is_a?(Array)
  32. 573942 str = str.to_s
  33. 573942 if !validated_parts && str.include?(joint)
  34. 70275 compose Cardname.split_parts(str)
  35. 503667 elsif (id = Card.id_from_string(str)) # handles ~ and :
  36. 10 Card.fetch_name(id) { Card.bad_mark(str) }
  37. else
  38. 503657 super str
  39. end
  40. end
  41. # interprets symbols/integers as codenames/ids
  42. 1 def smart_compose parts
  43. 11823 name_parts = parts.flatten.map { |part| self[part] }
  44. 3925 new name_parts.join(joint), true
  45. end
  46. 1 def compose parts
  47. 243520 name_parts = parts.flatten.map { |part| new part }
  48. 77230 new name_parts.join(joint), true
  49. end
  50. 1 def url_key_to_standard key
  51. 5354 key.to_s.tr "_", " "
  52. end
  53. end
  54. 1 def star?
  55. 7674 simple? && s[0, 1] == "*"
  56. end
  57. 1 def rstar?
  58. 7674 right && right[0, 1] == "*"
  59. end
  60. 1 def code
  61. 700 Card::Codename[Card.fetch_id self]
  62. end
  63. 1 def setting?
  64. Set::Type::Setting.member_names[key]
  65. end
  66. 1 def set?
  67. Set::Pattern.card_keys[tag_name.key]
  68. end
  69. end
  70. end

card/lib/card/name/fields_and_traits.rb

96.55% lines covered

29 relevant lines. 28 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 class Name
  3. # Name-based "Fields" are compound names in which the right name is treated
  4. # as an attribute of the left. (Eg MyName+address)
  5. #
  6. # "Traits" are the subset of fields in which the right name corresponds to a
  7. # card with a codename
  8. 1 module FieldsAndTraits
  9. # @return [String]
  10. 1 def field tag_name
  11. 214 field_name(tag_name).s
  12. end
  13. # @return [Card::Name]
  14. 1 def field_name tag_name
  15. 361 case tag_name
  16. when Symbol
  17. 204 trait_name tag_name
  18. else
  19. 157 tag_name = tag_name.to_s[1..-1] if tag_name.to_s[0] == "+"
  20. 157 [self, tag_name].to_name
  21. end
  22. end
  23. # @return [True/False]
  24. 1 def field_of? context
  25. 1054 return false unless junction?
  26. 1030 if context.present?
  27. 987 absolute_name(context).left_name.key == context.to_name.key
  28. else
  29. 43 s.match(/^\s*\+[^+]+$/).present?
  30. end
  31. end
  32. 1 def field_only?
  33. 21 relative? && stripped.to_name.parts.reject(&:blank?).first == parts.last
  34. end
  35. 1 def relative_field_name tag_name
  36. field_name(tag_name).name_from self
  37. end
  38. # @return [String]
  39. 1 def trait tag_code
  40. 3336 name = trait_name tag_code
  41. 3336 name.s
  42. end
  43. # @return [Card::Name]
  44. 1 def trait_name tag_code
  45. 3561 Card::Name[self, tag_code.to_sym]
  46. end
  47. # @return [True/False]
  48. 1 def trait_name? *traitlist
  49. 1400 return false unless junction?
  50. 1316 right_key = right_name.key
  51. 1316 traitlist.any? do |codename|
  52. 1316 Card::Codename.name(codename)&.key == right_key
  53. end
  54. end
  55. end
  56. end
  57. end

card/lib/card/name/name_variants.rb

58.82% lines covered

17 relevant lines. 10 lines covered and 7 lines missed.
    
  1. 1 class Card
  2. 1 class Name
  3. 1 module NameVariants
  4. 1 @@variant_methods = %i[capitalize singularize pluralize titleize
  5. downcase upcase swapcase reverse succ]
  6. 1 @@variant_aliases = { capitalized: :capitalize, singular: :singularize,
  7. plural: :pluralize, title: :titleize }
  8. 1 def vary variants
  9. variants.to_s.split(/[\s,]+/).inject(s) do |name, variant|
  10. variant = @@variant_aliases[variant.to_sym] || variant.to_sym
  11. @@variant_methods.include?(variant) ? name.send(variant) : name
  12. end
  13. end
  14. # @return [Card::Name] standardized based on card names
  15. 1 def standard
  16. self.class.compose(parts.map { |part| Card.fetch_name(part) || part })
  17. end
  18. 1 def card
  19. Card.fetch self, new: {}
  20. end
  21. # @return [Integer] id of card with name
  22. 1 def card_id
  23. Card.fetch_id self
  24. end
  25. # @return [Symbol] codename of card with name
  26. 1 def codename
  27. Codename[card_id]
  28. end
  29. end
  30. end
  31. end

card/lib/card/query.rb

100.0% lines covered

23 relevant lines. 23 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # Card::Query is for finding implicit lists (or counts of lists) of cards.
  4. #
  5. # Search and Set cards use Card::Query to query the database, and it's also
  6. # frequently used directly in code.
  7. #
  8. # Query "statements" (objects, really) are made in CQL (Card Query
  9. # Language). Because CQL is used by Sharks, the primary language
  10. # documentation is on decko.org. (https://decko.org/CQL_Syntax). Note that the
  11. # examples there are in JSON, like Search card content, but statements in
  12. # Card::Query are in ruby form.
  13. #
  14. # In Decko's current form, Card::Query generates and executes SQL statements.
  15. # However, the SQL generation is largely (not yet fully) separated from the
  16. # CQL statement interpretation.
  17. #
  18. # The most common way to use Card::Query is as follows:
  19. # list_of_cards = Card::Query.run(statement)
  20. #
  21. # This is equivalent to:
  22. # query = Card::Query.new(statement)
  23. # list_of_cards = query.run
  24. #
  25. # Upon initiation, the query is interpreted, and the following key objects
  26. # are populated:
  27. #
  28. # - @join - an Array of Card::Query::Join objects
  29. # - @conditions - an Array of conditions
  30. # - @mod - a Hash of other query-altering keys
  31. # - @subqueries - a list of other queries nested within this one
  32. #
  33. # Each condition is either a SQL-ready string (boo) or an Array in this form:
  34. # [ field_string_or_sym, Card::Value::Query object ]
  35. 1 module Query
  36. 1 require "card/query/clause"
  37. 1 require "card/query/card_query"
  38. 1 require "card/query/sql_statement"
  39. # Card::Query::CardQuery
  40. # After conversion, ATTRIBUTES is a Hash where the key is the attribute
  41. # and the value is the attribute type:
  42. # { id: :basic, name: :basic, key: :basic ...}
  43. # This is used for rapid attribute type lookups in the interpretation phase.
  44. ATTRIBUTES = {
  45. # Each of the "basic" fields corresponds directly to a database field.
  46. # their values are translated fairly directly into SQL-safe values.
  47. # (These are referred to as "properties" in CQL documentation. Need to
  48. # reconcile #EFM)
  49. 1 basic: %i[id name key type_id content left_id right_id
  50. creator_id updater_id codename read_rule_id],
  51. # "Relational" values can involve tying multiple queries together
  52. relational: %i[type
  53. part left right
  54. editor_of edited_by last_editor_of last_edited_by
  55. creator_of created_by
  56. updater_of updated_by
  57. link_to linked_to_by
  58. include included_by
  59. nest nested_by
  60. refer_to referred_to_by
  61. member_of member
  62. found_by
  63. not sort match name_match complete],
  64. plus_relational: %i[plus left_plus right_plus],
  65. conjunction: %i[and or all any],
  66. ignore: %i[prepend append vars],
  67. deprecated: %i[view params size]
  68. }.each_with_object({}) do |pair, h|
  69. 58 pair[1].each { |v| h[v] = pair[0] }
  70. end
  71. 1 CONJUNCTIONS = { any: :or, in: :or, or: :or, all: :and, and: :and }.freeze
  72. 1 MODIFIERS = %i[conj return sort sort_as group dir limit offset]
  73. 8 .each_with_object({}) { |v, h| h[v] = nil }
  74. OPERATORS =
  75. 9 %w[!= = =~ < > in ~ is].each_with_object({}) { |v, h| h[v] = v }.merge(
  76. { eq: "=", gt: ">", lt: "<", match: "~", ne: "!=",
  77. "not in": "not in", "is not": "is not", "!": "is not" }.stringify_keys
  78. )
  79. 1 DEFAULT_ORDER_DIRS = { update: "desc", relevance: "desc" }.freeze
  80. 1 class << self
  81. 1 def new statement, comment=nil
  82. 732 Query::CardQuery.new statement, comment
  83. end
  84. 1 def run statement, comment=nil
  85. 681 new(statement, comment).run
  86. end
  87. 1 def class_for type
  88. 1337 const_get "#{type.capitalize}Query"
  89. end
  90. 1 def safe_sql txt
  91. 750 txt = txt.to_s
  92. 750 raise "CQL contains disallowed characters: #{txt}" if txt.match?(/[^\w\s*().,]/)
  93. 750 txt
  94. end
  95. end
  96. end
  97. end

card/lib/card/query/abstract_query.rb

91.67% lines covered

48 relevant lines. 44 lines covered and 4 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. # superclass for CardQuery, ReferenceQuery, ActQuery, and ActionQuery
  4. #
  5. # Each of the Query classes handle interpretation of hash "statements"
  6. # into a number of objects known to the SqlStatement class, including
  7. # @conditions, @joins, @comment, and the catch-all @mods
  8. #
  9. # Sql queries involving multiple tables are made possible by the query
  10. # hierarchy as tracked by subqueries (children) and superqueries (parents).
  11. # For example, if one card links to another, then this can be represented
  12. # as a CardQuery with a ReferenceQuery child that in turn has another
  13. # CardQuery as its child.
  14. #
  15. # See AbstractQuery::Tie for more on how tables can be connected.
  16. 1 class AbstractQuery
  17. 1 include QueryHelper
  18. 1 include Tie
  19. 1 attr_reader :statement, :mods, :conditions, :vars,
  20. :subqueries, :superquery, :comment, :negate
  21. 1 attr_accessor :joins, :conditions_on_join
  22. 1 def initialize statement, _comment=nil
  23. 2069 @subqueries = []
  24. 2069 @conditions = []
  25. 2069 @joins = []
  26. 2069 @mods = {}
  27. 2069 @statement = statement.clone
  28. 2069 init_instance_vars :context, :superquery, :fasten, :negate
  29. 2069 @vars = init_query_vars
  30. 2069 table_alias
  31. end
  32. 1 def init_instance_vars *varnames
  33. 2069 varnames.each do |varname|
  34. 8276 instance_variable_set "@#{varname}", (@statement.delete(varname) || nil)
  35. end
  36. end
  37. 1 def init_query_vars
  38. 2069 if (v = @statement.delete :vars) then v.symbolize_keys
  39. 2062 elsif @superquery then @superquery.vars
  40. 717 else {}
  41. end
  42. end
  43. 1 def interpret hash
  44. 886 hash.each do |action, card|
  45. 886 send action, card
  46. end
  47. end
  48. 1 def full?
  49. false
  50. end
  51. 1 def sql
  52. 720 @sql ||= Query::SqlStatement.new(self).build.to_s
  53. end
  54. 1 def root
  55. 8133 @root ||= @superquery ? @superquery.root : self
  56. end
  57. 1 def root?
  58. 5459 root == self
  59. end
  60. 1 def subquery opts={}
  61. 1345 klass = opts.delete(:class) || Query
  62. 1345 subquery = klass.new opts.merge(superquery: self)
  63. 1345 @subqueries << subquery
  64. 1345 subquery
  65. end
  66. 1 def context
  67. 37 if !@context.nil?
  68. 37 @context
  69. else
  70. @context = superquery ? superquery.context : ""
  71. end
  72. end
  73. 1 def depth
  74. 2057 @depth ||= case
  75. 720 when !superquery then 0
  76. when fasten == :direct then superquery.depth
  77. else superquery.depth + 1
  78. end
  79. end
  80. end
  81. end
  82. end

card/lib/card/query/abstract_query/query_helper.rb

100.0% lines covered

30 relevant lines. 30 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class AbstractQuery
  4. # shared methods for queries
  5. 1 module QueryHelper
  6. 1 def direct_subqueries
  7. 2057 subqueries_with_fasten :direct
  8. end
  9. 1 def subqueries_with_fasten fasten
  10. 2065 list = []
  11. 2065 subqueries.each do |s|
  12. 1345 next unless Array.wrap(fasten).include? s.fasten
  13. 8 list << s
  14. 8 list += s.subqueries_with_fasten s.fasten
  15. end
  16. 2065 list
  17. end
  18. 1 def table_alias
  19. 9355 @table_alias ||= begin
  20. 2069 if fasten == :direct
  21. 8 @superquery.table_alias
  22. else
  23. 2061 "#{table_prefix}#{next_table_suffix}"
  24. end
  25. end
  26. end
  27. 1 def next_table_suffix
  28. 3398 return root.next_table_suffix unless root?
  29. 2061 @table_suffix = (@table_suffix || -1) + 1
  30. end
  31. 1 def fld field_name
  32. 465 "#{table_alias}.#{field_name}"
  33. end
  34. 1 def add_condition *args
  35. 1302 @conditions <<
  36. 1302 if args.size > 1
  37. 829 [args.shift, Query::Value.new(args.shift, self)]
  38. else
  39. 473 args[0]
  40. end
  41. end
  42. 1 def current_conjunction
  43. 886 "AND"
  44. end
  45. end
  46. end
  47. end
  48. end

card/lib/card/query/abstract_query/tie.rb

88.46% lines covered

52 relevant lines. 46 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class AbstractQuery
  4. # The "Tie" methods support tying two queries (CardQuery, ReferenceQuery, etc)
  5. # together. The "fasten" variable determines which tying strategy is used.
  6. #
  7. # We currently support three values for "fasten": :join, :exist, and :in
  8. #
  9. # In concept, here's how the different strategies would tie table A to table B
  10. # in SQL assuming A.id = B.a_id
  11. #
  12. # - :join ... FROM A JOIN B ON A.id = B.a_id
  13. # - :exist ... FROM A WHERE EXISTS (SELECT * FROM B WHERE A.id = B.a_id ...)
  14. # - :in ... FROM A WHERE A.id IN (SELECT B.a_id FROM B WHERE ...)
  15. #
  16. # The different strategies will return the same values but the relative speed is
  17. # context dependent.
  18. 1 module Tie
  19. 1 def tie subquery_type, val, fields={}, subquery_args={}
  20. 1337 subquery = tie_subquery subquery_type, subquery_args
  21. 1337 subquery.interpret val
  22. 1337 fields = { from: :id, to: :id }.merge fields
  23. 1337 fasten_tie subquery, fields
  24. end
  25. 1 def tie_subquery subquery_type, subquery_args
  26. 1337 subquery_args[:class] = Query.class_for subquery_type
  27. 1337 subquery(subquery_args)
  28. end
  29. 1 def fasten_tie subquery, fields={}
  30. 1337 method = "tie_with_#{subquery.fasten}"
  31. 1337 send method, subquery, fields
  32. 1337 subquery
  33. end
  34. 1 def tie_with_join subquery, fields={}
  35. 1337 join = Join.new tie_with_join_args(subquery, fields)
  36. 1337 negate_join(subquery, join, fields) if subquery.negate
  37. 1337 joins << join
  38. end
  39. 1 def tie_with_in subquery, fields
  40. subquery.mods[:return] = fields[:to]
  41. subquery.mods[:in_field] = fld(fields[:from])
  42. end
  43. 1 def tie_with_exist subquery, fields
  44. subquery.super_conditions fields if fields.present?
  45. end
  46. 1 def fasten
  47. 7424 @fasten ||= root? ? :join : inherit_fasten
  48. end
  49. 1 def tie_with_join_args subquery, fields
  50. 1337 args = { from: self, from_field: fields[:from],
  51. to: subquery, to_field: fields[:to] }
  52. 1337 args[:side] = :left if left_join? subquery
  53. 1337 args
  54. end
  55. 1 def left_join? subquery
  56. 1337 current_conjunction == "or" || subquery.negate
  57. # reverse conjunction if negated?
  58. end
  59. 1 def negate_join subquery, join, fields
  60. 17 subquery.conditions_on_join = join
  61. 17 add_condition "#{subquery.fld fields[:to]} is null"
  62. end
  63. 1 def inherit_fasten
  64. 1337 superfasten = superquery.fasten
  65. 1337 superfasten == :direct ? superquery.inherit_fasten : superfasten
  66. end
  67. 1 def super_conditions fields
  68. superfield fields[:to], fields[:from]
  69. end
  70. 1 def superfield myfield, superfield
  71. add_condition "#{fld myfield} = #{superquery.fld superfield}"
  72. end
  73. 1 def restrict id_field, val
  74. 195 if (id = id_from_val(val))
  75. 164 interpret id_field => id
  76. else
  77. 31 tie :card, val, from: id_field
  78. end
  79. end
  80. 1 def id_from_val val
  81. 419 case val
  82. 125 when Integer then val
  83. 263 when String then Card.fetch_id(val) || -999
  84. when Symbol then Card::Codename.id(val) || -999
  85. end
  86. end
  87. end
  88. end
  89. end
  90. end

card/lib/card/query/act_query.rb

84.62% lines covered

13 relevant lines. 11 lines covered and 2 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. # support CQL queries that require the card_acts table
  4. 1 class ActQuery < AbstractQuery
  5. 1 def table
  6. 662 "card_acts"
  7. end
  8. 1 def table_prefix
  9. 331 "cx"
  10. end
  11. 1 def action_on card
  12. 331 tie :action, { action_on: card }, to: :card_act_id
  13. end
  14. 1 def update_action_on card
  15. tie :action, { update_action_on: card }, to: :card_act_id
  16. end
  17. 1 def act_by card
  18. tie :card, card, from: :actor_id
  19. end
  20. end
  21. end
  22. end

card/lib/card/query/action_query.rb

68.42% lines covered

19 relevant lines. 13 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. # support CQL queries that require the card_acts table
  4. 1 class ActionQuery < AbstractQuery
  5. 1 def table
  6. 662 "card_actions"
  7. end
  8. 1 def table_prefix
  9. 331 "cn"
  10. end
  11. 1 def action_by card
  12. tie :act, { act_by: card }, from: :card_act_id
  13. end
  14. 1 def update_action_by card
  15. add_update_condition
  16. action_by card
  17. end
  18. 1 def action_on card
  19. 331 tie :card, card, from: :card_id
  20. end
  21. 1 def update_action_on card
  22. add_update_condition
  23. action_on card
  24. end
  25. 1 def add_update_condition
  26. add_condition "#{fld :action_type} = 1"
  27. end
  28. end
  29. end
  30. end

card/lib/card/query/card_query.rb

96.67% lines covered

30 relevant lines. 29 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. # interpret CQL queries, transform them into SQL, and run them.
  4. 1 class CardQuery < AbstractQuery
  5. 1 include Clause
  6. 1 include Run
  7. 1 include MatchAttributes
  8. 1 include RelationalAttributes
  9. 1 include ReferenceAttributes
  10. 1 include FoundBy
  11. 1 include Interpretation
  12. 1 include Normalization
  13. 1 include Sorting
  14. 1 include Conjunctions
  15. # Query Execution
  16. # By default a query returns card objects. This is accomplished by returning
  17. # a card identifier from SQL and then hooking into our caching system (see
  18. # Card::Fetch)
  19. 1 def self.viewable_sql
  20. Card::Query::SqlStatement.new.permission_conditions("cards")
  21. end
  22. 1 def table
  23. 1846 "cards"
  24. end
  25. 1 def table_prefix
  26. 1175 "c"
  27. end
  28. 1 def initialize statement, comment=nil
  29. 1183 super statement
  30. 1183 @comment = comment || default_comment
  31. 1183 interpret @statement
  32. end
  33. 1 def default_comment
  34. 816 return if @superquery || !Card.config.sql_comments
  35. 357 statement.to_s
  36. end
  37. # Query Hierarchy
  38. # @root, @subqueries, and @superquery are used to track a hierarchy of
  39. # query objects. This nesting allows to find, for example, cards that
  40. # link to cards that link to cards....
  41. 1 def limit
  42. 57 mods[:limit].to_i
  43. end
  44. 1 def full?
  45. 1440 !superquery && mods[:return] != "count"
  46. end
  47. end
  48. end
  49. end

card/lib/card/query/card_query/conjunctions.rb

92.31% lines covered

26 relevant lines. 24 lines covered and 2 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class CardQuery
  4. # conjoining conditions
  5. 1 module Conjunctions
  6. 1 def all val
  7. conjoin val, :and
  8. end
  9. 1 alias_method :and, :all
  10. 1 def any val
  11. 1 conjoin val, :or
  12. end
  13. 1 alias_method :or, :any
  14. 1 alias_method :in, :any
  15. 1 def not val
  16. 17 tie :card, val, { id: :id }, negate: true
  17. end
  18. 1 def current_conjunction
  19. 1007 @mods[:conj].blank? ? :and : @mods[:conj]
  20. end
  21. 1 private
  22. 1 def conjunction val
  23. 72 return unless [String, Symbol].member? val.class
  24. CONJUNCTIONS[val.to_sym]
  25. end
  26. 1 def conjoin val, conj
  27. 1 subquery = subquery fasten: :direct, conj: conj
  28. 1 conjoinable_val(val).each do |val_item|
  29. 2 subquery.interpret val_item
  30. end
  31. end
  32. 1 def conjoinable_val val
  33. 1 return val if val.is_a? Array
  34. 3 clause_to_hash(val).map { |key, value| { key => value } }
  35. end
  36. end
  37. end
  38. end
  39. end

card/lib/card/query/card_query/found_by.rb

90.48% lines covered

21 relevant lines. 19 lines covered and 2 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class CardQuery
  4. # nest independent query
  5. 1 module FoundBy
  6. 1 def found_by val
  7. 7 found_by_cards(val).compact.each do |card|
  8. 7 subquery found_by_subquery(card)
  9. end
  10. end
  11. 1 private
  12. 1 def found_by_subquery card
  13. 7 found_by_statement(card).merge(fasten: :direct, context: card.name)
  14. end
  15. 1 def found_by_statement card
  16. 7 card&.try(:cql_hash) || invalid_found_by_card!(card)
  17. end
  18. 1 def invalid_found_by_card! card
  19. raise Card::Error::BadQuery, '"found_by" value must be valid Search, ' \
  20. "but #{card.name} is a #{card.type_name}"
  21. end
  22. 1 def found_by_cards val
  23. 7 if val.is_a? Hash
  24. Query.run val
  25. else
  26. 7 fetch_found_by_cards val
  27. end
  28. end
  29. 1 def fetch_found_by_cards val
  30. 7 Array.wrap(val).map do |v|
  31. 7 Card.fetch v.to_name.absolute(context), new: {}
  32. end
  33. end
  34. end
  35. end
  36. end
  37. end

card/lib/card/query/card_query/interpretation.rb

75.51% lines covered

49 relevant lines. 37 lines covered and 12 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class CardQuery
  4. # Interpret CQL. Once interpreted, SQL can be generated.
  5. 1 module Interpretation
  6. 1 INTERPRET_METHOD = { basic: :add_condition,
  7. relational: :relate,
  8. plus_relational: :relate_compound,
  9. conjunction: :send }.freeze
  10. # normalize and extract meaning from a clause
  11. # @param clause [Hash, String, Integer] statement or chunk thereof
  12. 1 def interpret clause
  13. 1803 normalize_clause(clause).each do |key, val|
  14. 2264 interpret_item key, val
  15. end
  16. end
  17. 1 def interpret_item key, val
  18. 2264 if interpret_as_content? key
  19. 3 interpret content: [key, val]
  20. 2261 elsif interpret_as_modifier? key, val
  21. 574 interpret_modifier key, val
  22. else
  23. 1687 interpret_attributes key, val
  24. end
  25. end
  26. 1 def interpret_as_content? key
  27. # eg "match" is both operator and attribute;
  28. # interpret as attribute when "match" is key
  29. 2264 OPERATORS.key?(key.to_s) && !ATTRIBUTES[key]
  30. end
  31. 1 def interpret_as_modifier? key, val
  32. # eg when "sort" is hash, it can have subqueries
  33. # and must be interpreted like an attribute
  34. 2261 MODIFIERS.key?(key) && !val.is_a?(Hash)
  35. end
  36. 1 def interpret_modifier key, val
  37. 574 @mods[key] = val.is_a?(Array) ? val : val.to_s
  38. end
  39. 1 def interpret_attributes attribute, val
  40. 1687 attribute_type = ATTRIBUTES[attribute]
  41. 1687 if (method = INTERPRET_METHOD[attribute_type])
  42. 1687 send method, attribute, val
  43. else
  44. no_method_for_attribute_type attribute, attribute_type
  45. end
  46. end
  47. 1 def no_method_for_attribute_type attribute, type
  48. return if type == :ignore
  49. if type == :deprecated
  50. deprecated_attribute attribute
  51. else
  52. bad_attribute! attribute
  53. end
  54. end
  55. 1 def deprecated_attribute attribute
  56. Rails.logger.info "Card queries no longer support #{attribute} attribute"
  57. end
  58. 1 def bad_attribute! attribute
  59. raise Error::BadQuery, "Invalid attribute: #{attribute}"
  60. end
  61. 1 def relate_compound key, val
  62. has_multiple_values =
  63. 72 val.is_a?(Array) &&
  64. 72 (val.first.is_a?(Array) || conjunction(val.first).present?)
  65. 72 relate key, val, multiple: has_multiple_values
  66. end
  67. 1 def relate key, val, opts={}
  68. 858 multiple = opts[:multiple].nil? ? val.is_a?(Array) : opts[:multiple]
  69. 858 method = opts[:method] || :send
  70. 858 if multiple
  71. relate_multi_value method, key, val
  72. else
  73. 858 send method, key, val
  74. end
  75. end
  76. 1 def relate_multi_value method, key, val
  77. conj = conjunction(val.first) ? conjunction(val.shift) : :and
  78. if conj == current_conjunction
  79. # same conjunction as container, no need for subcondition
  80. val.each { |v| send method, key, v }
  81. else
  82. send conj, (val.map { |v| { key => v } })
  83. end
  84. end
  85. end
  86. end
  87. end
  88. end

card/lib/card/query/card_query/match_attributes.rb

85.0% lines covered

20 relevant lines. 17 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class CardQuery
  4. # Implements the match attributes that match always against content and/or name.
  5. # Currently that's different from the match operator that can be restricted to
  6. # names or content.
  7. # Example: { match: "name or content" } vs. { name: ["match", "a name"] }
  8. # TODO: unify handling for both using full text indexing
  9. 1 module MatchAttributes
  10. # match term anywhere in name or content
  11. 1 def match val
  12. 8 return unless val.present?
  13. 5 subconds = %i[name content].map do |field|
  14. 10 Value.new([:match, val], self).to_sql field
  15. end
  16. 5 add_condition or_join(subconds)
  17. end
  18. # match names beginning with term
  19. 1 def complete val
  20. 3 val = val.to_name
  21. 3 if val.junction?
  22. interpret left: val.left
  23. interpret right: { complete: val.right } if val.right.present?
  24. else
  25. 3 add_condition "#{table_alias}.key LIKE '#{val.to_name.key}%'"
  26. end
  27. end
  28. # match term anywhere in name
  29. # DEPRECATE - move handling to name: ["match", val]
  30. 1 def name_match val
  31. interpret name: [:match, val]
  32. end
  33. 1 private
  34. 1 def or_join conditions
  35. 5 "(#{Array(conditions).join ' OR '})"
  36. end
  37. end
  38. end
  39. end
  40. end

card/lib/card/query/card_query/normalization.rb

87.88% lines covered

33 relevant lines. 29 lines covered and 4 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class CardQuery
  4. # normalize clause's keys and values.
  5. 1 module Normalization
  6. 1 def normalize_clause clause
  7. 1803 clause = clause_to_hash clause
  8. 1803 clause.symbolize_keys!
  9. 1803 clause.each do |key, val|
  10. 2264 next if key.to_sym == :return
  11. # when return values are relative, they are relative to the name of the
  12. # card returned, not the context card
  13. 1773 clause[key] = normalize_value val
  14. end
  15. 1803 clause
  16. end
  17. 1 def clause_to_hash clause
  18. 1876 case clause
  19. 1545 when Hash then clause
  20. 331 when Integer then { id: clause }
  21. when String then { id: (Card::Lexicon.id(clause) || -2) }
  22. when Symbol then { id: Card::Codename.id(clause) }
  23. else raise Error::BadQuery, "Invalid clause: #{clause.inspect}"
  24. end
  25. end
  26. 1 def normalize_value val
  27. 2069 case val
  28. 1498 when Integer, Float, Hash, Symbol, NilClass then val
  29. 473 when String then normalize_string_value val
  30. 98 when Array then normalize_array_value val
  31. else raise Error::BadQuery, "Invalid value type: #{val.class} (#{val.inspect})"
  32. end
  33. end
  34. 1 def normalize_array_value val
  35. 394 val.map { |v| normalize_value v }
  36. end
  37. 1 def normalize_string_value val
  38. 473 case val.to_s
  39. when /^\$(\w+)$/
  40. # replace from @vars when value starts with dollar sign
  41. 8 string_value_from_var Regexp.last_match[1]
  42. when /\b_/
  43. # absolutize based on @context when there are words beginning with "_"
  44. 30 val.to_name.absolute(context)
  45. else
  46. 435 val
  47. end
  48. end
  49. 1 def string_value_from_var varname
  50. 8 @vars[varname.to_sym].to_s.strip
  51. end
  52. end
  53. end
  54. end
  55. end

card/lib/card/query/card_query/reference_attributes.rb

86.67% lines covered

15 relevant lines. 13 lines covered and 2 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class CardQuery
  4. # interpret CQL attributes that involve references from one card to another
  5. 1 module ReferenceAttributes
  6. 1 def self.define_reference_method methodname, reftype, ref_method, ref_field
  7. 8 define_method methodname do |val|
  8. 224 tie :reference,
  9. { ref_method => { reftype: reftype, card: val } },
  10. to: ref_field
  11. end
  12. end
  13. {
  14. 1 refer_to: %w[L I],
  15. link_to: "L",
  16. include: "I",
  17. nest: "I"
  18. }.each do |methodname, reftype|
  19. 4 define_reference_method methodname, reftype, :referee, :referer_id
  20. end
  21. {
  22. 1 referred_to_by: %w[L I],
  23. linked_to_by: "L",
  24. included_by: "I",
  25. nested_by: "I"
  26. }.each do |methodname, reftype|
  27. 4 define_reference_method methodname, reftype, :referer, :referee_id
  28. end
  29. # shortcut methods for role references
  30. # DEPRECATE?
  31. 1 def member_of val
  32. interpret right_plus: [Card::RolesID, refer_to: val]
  33. end
  34. 1 def member val
  35. interpret referred_to_by: { left: val, right: Card::RolesID }
  36. end
  37. end
  38. end
  39. end
  40. end

card/lib/card/query/card_query/relational_attributes.rb

77.78% lines covered

45 relevant lines. 35 lines covered and 10 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class CardQuery
  4. # interpret CQL attributes that relate multiple cards
  5. # each method below corresponds to a relational CQL term
  6. #
  7. # NOTE: methods using "restrict" can be executed without
  8. # tying in an additional query if the val in question can be
  9. # reduced to an id.
  10. 1 module RelationalAttributes
  11. 1 def type val
  12. 64 restrict :type_id, val
  13. end
  14. 1 def part val
  15. 1 right_val = val.is_a?(Integer) ? val : val.clone
  16. 1 any(left: val, right: right_val)
  17. end
  18. 1 def left val
  19. 31 restrict :left_id, val
  20. end
  21. 1 def right val
  22. 100 restrict :right_id, val
  23. end
  24. 1 def editor_of val
  25. 331 tie_act :action_on, val
  26. end
  27. 1 def updater_of val
  28. tie_act :update_action_on, val
  29. end
  30. 1 def edited_by val
  31. tie_action :action_by, val
  32. end
  33. 1 def updated_by val
  34. tie_action :update_action_by, val
  35. end
  36. 1 def last_editor_of val
  37. tie :card, val, to: :updater_id
  38. end
  39. 1 def last_edited_by val
  40. restrict :updater_id, val
  41. end
  42. 1 def creator_of val
  43. tie :card, val, to: :creator_id
  44. end
  45. 1 def created_by val
  46. restrict :creator_id, val
  47. end
  48. # ~~~~~~ PLUS RELATIONAL
  49. 1 def left_plus val
  50. junction val, :left, :right_id
  51. end
  52. 1 def right_plus val
  53. 72 junction val, :right, :left_id
  54. end
  55. 1 def plus val
  56. any(left_plus: val, right_plus: val.deep_clone)
  57. end
  58. 1 private
  59. 1 def tie_action action, val
  60. tie :action, { action => val }, to: :card_id
  61. end
  62. 1 def tie_act action, val
  63. 331 tie :act, { action => val }, to: :actor_id
  64. end
  65. 1 def junction val, side, field
  66. 72 tie :card, junction_val(val, side), to: field
  67. end
  68. 1 def junction_val val, side
  69. 72 part_clause, junction_clause = val.is_a?(Array) ? val : [val, {}]
  70. 72 clause_to_hash(junction_clause).merge side => part_clause
  71. end
  72. end
  73. end
  74. end
  75. end

card/lib/card/query/card_query/run.rb

81.67% lines covered

60 relevant lines. 49 lines covered and 11 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class CardQuery
  4. # run CQL queries
  5. 1 module Run
  6. # run the current query
  7. # @return [Array] of card objects by default
  8. 1 def run
  9. 720 retrn = statement[:return].present? ? statement[:return].to_s : "card"
  10. 720 return_method = :"return_#{simple_result?(retrn) ? :simple : :list}"
  11. 720 send return_method, run_sql, retrn
  12. end
  13. # @return [(not an Array)]
  14. 1 def return_simple sql_result, retrn
  15. 1135 send result_method(retrn), sql_result, retrn
  16. end
  17. # @return [Array]
  18. 1 def return_list sql_results, retrn
  19. 690 large_list sql_results.length if sql_results.length > 1000
  20. 690 sql_results.map do |record|
  21. 1105 return_simple record, retrn
  22. end
  23. end
  24. 1 def large_list length
  25. Rails.logger.info "#{length} records returned by #{@statement}"
  26. end
  27. 1 def result_method retrn
  28. case
  29. 2270 when respond_to?(:"#{retrn}_result") then :"#{retrn}_result"
  30. when (retrn =~ /id$/) then :id_result
  31. when (retrn =~ /_\w+/) then :name_result
  32. when (retrn == "key") then :key_result
  33. else :default_result
  34. end
  35. end
  36. 1 def count_result results, _field
  37. 30 results.first["count"].to_i
  38. end
  39. 1 def default_result record, field
  40. record[field]
  41. end
  42. 1 def id_result record, field
  43. 349 record[field].to_i
  44. end
  45. 1 def raw_result record, _field
  46. record
  47. end
  48. 1 def key_result record, pattern=""
  49. 22 name_result(record, pattern).to_name.key
  50. end
  51. 1 def name_result record, pattern=""
  52. 633 name = record["name"]&.to_name
  53. 633 name ||= Card::Lexicon.lex_to_name [record["left_id"], record["right_id"]]
  54. 633 process_name name, pattern
  55. end
  56. 1 def card_result record, _field
  57. 123 if alter_results?
  58. Card.fetch name_result(record), new: {}
  59. else
  60. 123 fetch_or_instantiate record
  61. end
  62. end
  63. 1 def fetch_or_instantiate record
  64. 123 card = Card.retrieve_from_cache_by_id record["id"]
  65. 123 unless card
  66. 44 card = Card.instantiate record
  67. 44 Card.write_to_cache card
  68. end
  69. 123 card.include_set_modules
  70. 123 card
  71. end
  72. 1 def run_sql
  73. # puts "\nSQL = #{sql}"
  74. 720 ActiveRecord::Base.connection.select_all sql
  75. end
  76. 1 def process_name name, pattern=""
  77. 633 name = pattern.to_name.absolute(name) if pattern =~ /_\w+/
  78. 633 return name unless alter_results?
  79. alter_result name
  80. end
  81. 1 def alter_result name
  82. name_parts = [statement[:prepend], name, statement[:append]].compact
  83. Card::Name[name_parts]
  84. end
  85. 1 def simple_result? retrn
  86. 720 retrn == "count"
  87. end
  88. 1 def alter_results?
  89. 756 statement[:prepend] || statement[:append]
  90. end
  91. end
  92. end
  93. end
  94. end

card/lib/card/query/card_query/sorting.rb

32.65% lines covered

49 relevant lines. 16 lines covered and 33 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class CardQuery
  4. # handle relational (not simple) CQL sort values
  5. #
  6. # sorting with subqueries is not fully supported; only a few experimental
  7. # examples have been attempted, and even for those the syntax is likely
  8. # to change.
  9. #
  10. # Generally speaking, sorting subqueries will require a JOIN strategy (as
  11. # opposed to the "WHERE EXISTS" strategy that is central to queries' main
  12. # conditions.)
  13. 1 module Sorting
  14. 1 SORT_BY_ITEM_JOIN_MAP = { left: "left_id", right: "right_id" }.freeze
  15. 1 def sort val
  16. return nil unless full?
  17. sort_field = val[:return] || "db_content"
  18. val = val.clone
  19. item = val.delete(:item) || "left"
  20. sort_by val, item, sort_field
  21. end
  22. 1 def sort_by val, item, sort_field
  23. if sort_field == "count"
  24. sort_by_count val, item
  25. else
  26. sort_by_item_join val, item, sort_field
  27. end
  28. end
  29. 1 def sort_by_item_join val, item, sort_field
  30. join_field = sort_by_item_join_field item
  31. join = join_cards val, to_field: join_field,
  32. side: "LEFT",
  33. conditions_on_join: true
  34. @mods[:sort] ||= "#{join.table_alias}.#{sort_field}"
  35. end
  36. 1 def sort_by_item_join_field item
  37. SORT_BY_ITEM_JOIN_MAP[item.to_sym] || sort_method_not_implemented(:join, item)
  38. end
  39. # EXPERIMENTAL!
  40. # sort: { referred_to_by { right: "*follow" }, return count }
  41. 1 def sort_by_count val, item
  42. method_name = "sort_by_count_#{item}"
  43. sort_method_not_implemented :count, item unless respond_to? method_name
  44. send method_name, val
  45. end
  46. 1 def sort_method_not_implemented method, item
  47. raise Card::Error::BadQuery, "sorting by ##{method}/#{item} not yet implemented"
  48. end
  49. 1 def sort_by_count_referred_to val
  50. @mods[:sort] = "coalesce(count,0)" # needed for postgres
  51. sort_query = count_sort_query
  52. sort_query.add_condition "referer_id in (#{count_subselect(val).sql})"
  53. # FIXME: - SQL generated before SQL phase
  54. sort_query.joins << Join.new(from: sort_query, side: "LEFT",
  55. to: %w[card_references wr referee_id])
  56. join_count_sort_query sort_query
  57. end
  58. 1 def join_count_sort_query sort_query
  59. sort_query.mods[:sort_join_field] =
  60. "#{sort_query.table_alias}.id as sort_join_field"
  61. # FIXME: HACK!
  62. joins << Join.new(from: self, side: "LEFT",
  63. to: [sort_query, "srtbl", "sort_join_field"])
  64. end
  65. 1 def count_subselect val
  66. Query.new val.merge(return: "id", superquery: self)
  67. end
  68. 1 def count_sort_query
  69. Query.new return: "coalesce(count(*), 0) as count",
  70. group: "sort_join_field",
  71. superquery: self
  72. end
  73. 1 def join_cards val, opts={}
  74. conditions_on_join = opts.delete :conditions_on_join
  75. s = subquery
  76. join_opts = { from: self, to: s }.merge opts
  77. card_join = Join.new join_opts
  78. joins << card_join unless opts[:from].is_a? Join
  79. s.conditions_on_join = card_join if conditions_on_join
  80. s.interpret val
  81. s
  82. end
  83. end
  84. end
  85. end
  86. end

card/lib/card/query/clause.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 module Clause
  4. # attr_accessor :clause
  5. 1 def safe_sql text
  6. 750 Query.safe_sql text
  7. end
  8. 1 def quote v
  9. 935 connection.quote(v)
  10. end
  11. 1 def connection
  12. 945 @connection ||= ActiveRecord::Base.connection
  13. end
  14. end
  15. end
  16. end

card/lib/card/query/join.rb

82.0% lines covered

50 relevant lines. 41 lines covered and 9 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. # object representation of Card::Query joins
  4. 1 class Join
  5. 1 JOIN_OPT_KEYS = %i[side conditions
  6. from from_table from_alias from_field
  7. to to_table to_alias to_field].freeze
  8. 1 attr_accessor(*JOIN_OPT_KEYS)
  9. # These two manage hierarchy of nested joins
  10. 1 attr_accessor :superjoin, :subjoins
  11. # This example join clause:
  12. #
  13. # cards c LEFT JOIN card_actions ca on c.id = ca.card_id and ca.draft is null
  14. #
  15. # ...would translate into the following instance variables on the Join object:
  16. #
  17. # @side = "left"
  18. # @from_table = "cards"
  19. # @from_alias = "c"
  20. # @from_field = "id"
  21. # @to_table = "card_actions"
  22. # @to_alias = "ca"
  23. # @to_field = "card_id"
  24. # @conditions = "ca.draft is null"
  25. #
  26. # all of the above can be set directly via opts using the keys with the same name.
  27. #
  28. # Join.new side: "left", from_table: "cards"...
  29. #
  30. # The from and to fields can also be set via :from and :to keys.
  31. # (see #interpret_from_and_to)
  32. #
  33. # You can generally use Symbols in place of Strings where applicable.
  34. #
  35. 1 def initialize opts={}
  36. 1337 interpret_from_and_to opts
  37. 1337 convert_opts_to_instance_variables opts
  38. 1337 @conditions = Array(@conditions).compact
  39. 1337 @subjoins = []
  40. 1337 register_superjoin
  41. end
  42. 1 def side
  43. 2674 if !@side.nil?
  44. 34 @side.to_s.upcase
  45. else
  46. 2640 @side = inside_or? ? "LEFT" : nil
  47. end
  48. end
  49. 1 def left?
  50. 1337 side == "LEFT"
  51. end
  52. 1 private
  53. 1 def inside_or?
  54. 2640 from&.is_a?(Card::Query) && from.mods[:conj] == "or"
  55. end
  56. # the options :to and :from can be translated into the full table/alias/field trio.
  57. #
  58. # - An Array is interpreted in that order (table, alias, field)
  59. # - A Hash expects the keys :table, :alias, and (optionally) :field
  60. # - A table and alias can be inferred from Card::Query or Card::Query::Reference
  61. # objects.
  62. # - They can also be inferred from a Join object, but only as a :from value
  63. #
  64. # In all cases, if the field is not specified, it is assumed to be :id
  65. 1 def interpret_from_and_to opts
  66. 1337 %i[from to].each do |side|
  67. 2674 directional_hash_for_object(side, opts[side]).map do |key, value|
  68. 8022 opts[:"#{side}_#{key}"] ||= value
  69. end
  70. end
  71. end
  72. 1 def directional_hash_for_object side, object
  73. 2674 case object
  74. when nil then nil
  75. when Hash then object
  76. when Array then dir_hash(*object)
  77. 2674 when AbstractQuery then dir_hash_for_query object
  78. when Join then dir_hash_for_join side, object
  79. else dir_error(side, object)
  80. end
  81. end
  82. 1 def dir_hash table, table_alias, field=nil
  83. 2674 hash = { table: table, alias: table_alias }
  84. 2674 hash[:field] = field || :id
  85. 2674 hash
  86. end
  87. 1 def dir_hash_for_query query
  88. 2674 dir_hash query.table, query.table_alias
  89. end
  90. 1 def dir_hash_for_join side, object
  91. raise "to: cannot be Join" if side == :to
  92. dir_hash object.to_table, object.to_alias
  93. end
  94. 1 def dir_error side, object
  95. raise Card::Error::BadQuery, "invalid #{side} option: #{object}"
  96. end
  97. 1 def convert_opts_to_instance_variables opts
  98. 1337 opts.each do |key, value|
  99. 10713 send "#{key}=", value if value.present? && JOIN_OPT_KEYS.member?(key)
  100. end
  101. end
  102. 1 def register_superjoin
  103. 1337 return unless @from.is_a? Join
  104. @superjoin = @from
  105. @superjoin.subjoins << self
  106. end
  107. end
  108. end
  109. end

card/lib/card/query/reference_query.rb

90.0% lines covered

30 relevant lines. 27 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. # support the use of the card_references table in CQL
  4. 1 class ReferenceQuery < AbstractQuery
  5. 1 def table
  6. 224 "card_references"
  7. end
  8. 1 def table_prefix
  9. 224 "cr"
  10. end
  11. 1 def referer hash
  12. 88 add_conditions :referer_id, hash
  13. end
  14. 1 def referee hash
  15. 136 add_conditions :referee_id, hash
  16. end
  17. 1 def add_conditions outfield, hash
  18. 224 add_reftype_condition hash[:reftype]
  19. 224 add_outfield_condition outfield, hash[:card]
  20. end
  21. 1 def add_outfield_condition outfield, outcard
  22. 224 if outcard == "_none"
  23. non_outfield
  24. 224 elsif (id = id_from_val(outcard))
  25. 224 outfield_id outfield, id
  26. else
  27. tie :card, outcard, from: outfield
  28. end
  29. end
  30. 1 def non_outfield
  31. add_condition "#{fld :is_present} = 0"
  32. end
  33. 1 def outfield_id outfield, id
  34. 224 add_condition "#{fld(outfield)} = #{id}"
  35. end
  36. 1 def add_reftype_condition reftype
  37. 224 return unless reftype.present?
  38. 224 reftype = Array.wrap reftype
  39. 224 operator = (reftype.size == 1 ? "=" : "IN")
  40. 448 quoted_letters = reftype.map { |letter| "'#{letter}'" } * ", "
  41. 224 add_condition "#{fld(:ref_type)} #{operator} (#{quoted_letters})"
  42. end
  43. end
  44. end
  45. end

card/lib/card/query/sql_statement.rb

92.42% lines covered

66 relevant lines. 61 lines covered and 5 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. # The SqlStatement class generates sql from the Query classes. However, the logic
  4. # is not yet as cleanly separated as it should be.
  5. # At present, SqlStatement contains (imho) too much knowledge about card constructs.
  6. # For example, all the permission and trash handling is here.
  7. #
  8. # In principle, the Query class should "interpret" statements into a few objects and
  9. # a clean Query hierarchy. The SqlStatement class should be able to traverse that
  10. # hierarchy and do little more than run "to_sql" on its parts, and in so doing
  11. # construct a valid SQL statement.
  12. 1 class SqlStatement
  13. 1 include Joins
  14. 1 include Where
  15. 1 include Order
  16. 1 def initialize query=nil
  17. 720 @query = query
  18. 720 @mods = query&.mods
  19. end
  20. 1 def build
  21. 720 @fields = fields
  22. 720 @tables = tables
  23. 720 @joins = joins
  24. 720 @where = where
  25. 720 @group = group
  26. 720 @order = order
  27. 720 @limit_and_offset = limit_and_offset
  28. 720 self
  29. end
  30. 1 def to_s
  31. [
  32. 720 comment, select, from, @joins, @where, @group, @order, @limit_and_offset
  33. ].compact.join " "
  34. end
  35. 1 def select
  36. 720 "#{leading_space}SELECT DISTINCT #{@fields}"
  37. end
  38. 1 def from
  39. 720 "FROM #{@tables}"
  40. end
  41. 1 def leading_space
  42. 2057 " " * (@query.depth * 2)
  43. end
  44. 1 def comment
  45. 720 return nil unless Card.config.sql_comments && @query.comment
  46. 720 "/* #{@query.comment} */\n"
  47. end
  48. 1 def tables
  49. 720 "#{@query.table} #{@query.table_alias}"
  50. end
  51. 1 def fields
  52. 720 table = @query.table_alias
  53. 720 field = @mods[:return] unless @mods[:return] =~ /^_\w+/
  54. 720 field = field.blank? ? :card : field.to_sym
  55. 720 field = full_field(table, field)
  56. 720 [field, @mods[:sort_join_field]].compact * ", "
  57. end
  58. 1 def full_field table, field
  59. 720 case field
  60. 229 when :card, :raw then "#{table}.*"
  61. when :content then "#{table}.db_content"
  62. 119 when :name, :key then "#{table}.name, #{table}.left_id, #{table}.right_id"
  63. 30 when :count then "coalesce(count( distinct #{table}.id),0) as count"
  64. else
  65. 342 standard_full_field table, field
  66. end
  67. end
  68. 1 def standard_full_field table, field
  69. 342 if ATTRIBUTES[field.to_sym] == :basic
  70. 342 "#{table}.#{field}"
  71. else
  72. safe_sql field
  73. end
  74. end
  75. 1 def group
  76. 720 group = @mods[:group]
  77. 720 "GROUP BY #{safe_sql group}" if group.present?
  78. end
  79. 1 def limit_and_offset
  80. 720 full_syntax do
  81. 690 limit = @mods[:limit]
  82. 690 offset = @mods[:offset]
  83. 690 if limit.to_i.positive?
  84. 14 string = "LIMIT #{limit.to_i} "
  85. 14 string += "OFFSET #{offset.to_i} " if offset.present?
  86. 14 string
  87. end
  88. end
  89. end
  90. 1 def full_syntax
  91. 1440 @query.full? ? yield : return
  92. end
  93. 1 def safe_sql txt
  94. Query.safe_sql txt
  95. end
  96. 1 def cast_type type
  97. cxn ||= ActiveRecord::Base.connection
  98. (val = cxn.cast_types[type.to_sym]) ? val[:name] : safe_sql(type)
  99. end
  100. end
  101. end
  102. end

card/lib/card/query/sql_statement/joins.rb

97.3% lines covered

37 relevant lines. 36 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class SqlStatement
  4. # transform joins from Card::Query::Join objects to SQL string clause
  5. 1 module Joins
  6. 1 def joins query=nil
  7. 2057 query ||= @query
  8. 2057 joins_on_query(query).map do |join|
  9. 1337 join_clause join
  10. end.flatten.join " "
  11. end
  12. 1 def joins_on_query query
  13. 2057 query.direct_subqueries.unshift(query).map(&:joins).flatten
  14. end
  15. 1 def join_clause join
  16. 1337 subclause = subjoins join
  17. 1337 table = join_table join
  18. 1337 on = on_clause join
  19. 1337 join_clause_parts(join, table, subclause, on).compact.join " "
  20. end
  21. 1 def join_clause_parts join, table, subclause, on
  22. 1337 parts = ["\n#{leading_space}", join.side, "JOIN"]
  23. 1337 if join.left? && subclause.present?
  24. parts + ["(#{table} #{subclause})", on]
  25. else
  26. 1337 parts + [table, on, subclause]
  27. end
  28. end
  29. 1 def subjoins join
  30. 1337 return unless join.to.is_a? AbstractQuery
  31. 1337 joins join.to
  32. end
  33. 1 def join_table join
  34. 1337 to_table = join.to_table
  35. 1337 to_table = "(#{to_table.sql})" if to_table.is_a? CardQuery
  36. 1337 [to_table, join.to_alias].join " "
  37. end
  38. 1 def on_clause join
  39. 1337 on_conditions = join.conditions
  40. 1337 on_conditions.unshift ["#{join.from_alias}.#{join.from_field}",
  41. "#{join.to_alias}.#{join.to_field}"].join(" = ")
  42. 1337 on_conditions += on_card_conditions(join) if join.to.is_a? CardQuery
  43. 1337 on_conditions.reject!(&:blank?)
  44. 1337 "ON #{basic_conditions(on_conditions) * ' AND '}"
  45. end
  46. 1 def on_card_conditions join
  47. 451 to = join.to
  48. 451 explicit = to.conditions_on_join == join ? explicit_conditions(to) : nil
  49. 451 [explicit, implicit_conditions(to)].compact
  50. end
  51. end
  52. end
  53. end
  54. end

card/lib/card/query/sql_statement/order.rb

90.0% lines covered

30 relevant lines. 27 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. # convert @query sort rep into order by statement
  4. # order information is stored in @mods[:sort], @mods[:sort_as], and
  5. # @mods[:dir]
  6. 1 class SqlStatement
  7. ORDER_MAP = {
  8. 1 "id" => "id",
  9. "update" => "updated_at",
  10. "create" => "created_at",
  11. "name" => "key",
  12. "content" => "db_content",
  13. "alpha" => "key", # DEPRECATED
  14. "relevance" => "updated_at" # DEPRECATED
  15. }.freeze
  16. # build ORDER BY clause
  17. 1 module Order
  18. 1 def order
  19. 720 full_syntax do
  20. 690 "ORDER BY #{order_directives.join ', '}"
  21. end
  22. end
  23. 1 def order_directives
  24. 690 Array.wrap(order_config).map do |order_key|
  25. 690 order_directive order_key
  26. end
  27. end
  28. 1 def order_directive order_key
  29. 690 field = order_field order_key
  30. 690 @fields += ", #{field}"
  31. 690 "#{field} #{order_dir order_key}"
  32. end
  33. 1 def order_field order_key
  34. 690 order_as do
  35. 690 if (field = ORDER_MAP[order_key])
  36. 690 "#{@query.table_alias}.#{field}"
  37. else
  38. safe_sql order_key
  39. end
  40. end
  41. end
  42. 1 def order_as
  43. 690 field = yield
  44. 690 return field unless (as = @mods[:sort_as])
  45. "CAST(#{field} AS #{cast_type(safe_sql(as))})"
  46. end
  47. 1 def order_dir order_key
  48. 690 if @mods[:dir].blank?
  49. 690 DEFAULT_ORDER_DIRS[order_key.to_sym] || "asc"
  50. else
  51. safe_sql @mods[:dir]
  52. end
  53. end
  54. 1 def order_config
  55. 690 @mods[:sort].blank? ? "update" : @mods[:sort]
  56. end
  57. end
  58. end
  59. end
  60. end

card/lib/card/query/sql_statement/where.rb

87.04% lines covered

54 relevant lines. 47 lines covered and 7 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class SqlStatement
  4. # handle where clause in SqlStatement
  5. 1 module Where
  6. 1 def where
  7. 720 conditions = [explicit_conditions(@query), implicit_conditions(@query)]
  8. 720 conditions = conditions.reject(&:blank?).join " AND "
  9. 720 "WHERE #{conditions}" unless conditions.blank?
  10. end
  11. # conditions explicitly specified in the query object
  12. 1 def explicit_conditions query
  13. 2065 cond_list = basic_conditions query.conditions
  14. 2065 cond_list += conditions_from query.subqueries
  15. 2065 cond_list.reject!(&:blank?)
  16. 2065 format_conditions cond_list, query
  17. end
  18. # depending on how a query is "fastened", its conditions may be rendered
  19. # along with the superquery's
  20. 1 def conditions_from subqueries
  21. 2065 subqueries.map do |query|
  22. 1345 next if query.conditions_on_join
  23. 1328 case query.fasten
  24. when :exist then exist_condition query
  25. when :in then in_condition query
  26. 1328 else explicit_conditions query
  27. end
  28. end
  29. end
  30. 1 def exist_condition query
  31. "#{maybe_not query}EXISTS (#{spaced_subquery_sql query})"
  32. end
  33. 1 def maybe_not query
  34. query.negate ? "NOT " : ""
  35. end
  36. 1 def in_condition query
  37. field = query.mods[:in_field]
  38. "#{field} #{maybe_not query}IN (#{spaced_subquery_sql query})"
  39. end
  40. 1 def spaced_subquery_sql subquery
  41. "\n#{subquery.sql}\n#{leading_space}"
  42. end
  43. # the conditions stored in the query's @conditions variable
  44. 1 def basic_conditions conditions
  45. 3402 conditions.map do |condition|
  46. 3103 case condition
  47. 2278 when String then condition
  48. 825 when Array then standard_condition(condition)
  49. end
  50. end
  51. end
  52. 1 def standard_condition condition
  53. 825 field, val = condition
  54. 825 val.to_sql field
  55. end
  56. # handle trash and permissions
  57. # only applies to card queries
  58. 1 def implicit_conditions query
  59. 1171 return unless query.is_a?(CardQuery)
  60. 1171 table = query.table_alias
  61. 1171 [trash_condition(table), permission_conditions(table)].compact * " AND "
  62. end
  63. 1 def trash_condition table
  64. 1171 "#{table}.trash is false"
  65. end
  66. 1 def permission_conditions table
  67. 1171 return if Auth.always_ok?
  68. 40 read_rules = Auth.as_card.read_rules
  69. 40 read_rule_list = read_rules.present? ? read_rules.join(",") : 1
  70. 40 "#{table}.read_rule_id IN (#{read_rule_list})"
  71. end
  72. # convert list of conditions to string
  73. 1 def format_conditions cond_list, query
  74. 2065 if cond_list.size > 1
  75. 556 "(#{cond_list.join condition_joint(query)})"
  76. else
  77. 1509 cond_list.join
  78. end
  79. end
  80. 1 def condition_joint query
  81. 556 " #{query.current_conjunction.upcase} "
  82. end
  83. end
  84. end
  85. end
  86. end

card/lib/card/query/value.rb

88.68% lines covered

53 relevant lines. 47 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. # handling for CQL value clauses, eg [operator, value]
  4. 1 class Value
  5. 1 include Clause
  6. 1 include MatchValue
  7. 1 SQL_FIELD = { name: "key", content: "db_content" }.freeze
  8. 1 attr_reader :query, :operator, :value
  9. 1 def initialize rawvalue, query
  10. 839 @query = query
  11. 839 @operator, @value = parse_value rawvalue
  12. 839 canonicalize_operator
  13. end
  14. 1 def to_sql field
  15. 835 if @operator == "~"
  16. 10 match_sql field
  17. else
  18. 825 standard_sql field
  19. end
  20. end
  21. 1 private
  22. 1 def standard_sql field
  23. 825 @value = Array.wrap(@value).map { |v| v.to_name.key } if field.to_sym == :name
  24. 825 "#{field_sql field} #{@operator} #{sqlize @value}"
  25. end
  26. 1 def parse_value value
  27. 839 case value
  28. 36 when Array then parse_array_value value.clone
  29. when nil then ["is", nil]
  30. 803 else ["=", parse_simple_value(value)]
  31. end
  32. end
  33. 1 def parse_array_value array
  34. 36 operator = operator?(array.first) ? array.shift : :in
  35. 172 [operator, array.flatten.map { |i| parse_simple_value i }]
  36. end
  37. 1 def parse_simple_value value
  38. 939 case value
  39. 939 when String, Integer then value
  40. when Symbol then value.to_s
  41. when nil then nil
  42. else raise Error::BadQuery, "Invalid property value: #{value.inspect}"
  43. end
  44. end
  45. 1 def canonicalize_operator
  46. 839 unless (target = OPERATORS[@operator.to_s])
  47. raise Error::BadQuery, "Invalid operator: #{@operator}"
  48. end
  49. 839 @operator = target
  50. end
  51. 1 def operator? key
  52. 36 OPERATORS.key? key.to_s
  53. end
  54. 1 def sqlize v
  55. 951 case v
  56. when Query then v.to_sql
  57. 26 when Array then sqlize_array v
  58. when nil then "NULL"
  59. 925 else quote(v.to_s)
  60. end
  61. end
  62. 1 def sqlize_array array
  63. 26 array.flatten!
  64. 26 if array.size == 1 && !@operator.in?(["in", "not in"])
  65. 10 sqlize array.first
  66. else
  67. 132 "(#{array.map { |x| sqlize(x) }.join(',')})"
  68. end
  69. end
  70. 1 def field_sql field
  71. 825 "#{@query.table_alias}.#{standardize_field field}"
  72. end
  73. 1 def standardize_field field
  74. 830 SQL_FIELD[field.to_sym] || safe_sql(field.to_s)
  75. end
  76. end
  77. end
  78. end

card/lib/card/query/value/match_value.rb

96.77% lines covered

31 relevant lines. 30 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Query
  3. 1 class Value
  4. 1 class << self
  5. 1 def match_prefices
  6. 1 @match_prefices ||= %w[= ~]
  7. end
  8. 1 def match_term_and_prefix_re
  9. 10 @match_term_and_prefix_re ||=
  10. /^(?<prefix>[#{Regexp.escape match_prefices.join}]*)\s*(?<term>.*)$/
  11. end
  12. end
  13. # handling for match operator
  14. 1 module MatchValue
  15. 1 def match_sql field
  16. 10 exact_name_match(field) ||
  17. "#{match_field field} #{connection.match match_value}"
  18. end
  19. 1 def exact_name_match field
  20. 10 return false unless match_prefix == "=" && field.to_sym == :name
  21. "#{field_sql field} = #{quote match_term.to_name.key}"
  22. end
  23. 1 def match_field field
  24. 10 fld = field.to_sym == :name ? "name" : standardize_field(field)
  25. 10 "#{@query.table_alias}.#{fld}"
  26. end
  27. 1 def match_value
  28. 10 escape_regexp_characters unless match_prefix == "~~"
  29. 10 quote match_term
  30. end
  31. 1 def match_term
  32. 20 @match_term || (parse_match_term_and_prefix && @match_term)
  33. end
  34. 1 def match_prefix
  35. 20 @match_prefix || (parse_match_term_and_prefix && @match_prefix)
  36. end
  37. # if search val is prefixed with "~~", it is a MYSQL regexp
  38. # (and will be passed through)
  39. #
  40. # Otherwise, all non-alphanumeric characters are escaped.
  41. #
  42. # A "~" prefix is ignored.
  43. 1 def parse_match_term_and_prefix
  44. 10 raw_term = Array.wrap(@value).join(" ")
  45. 10 matches = raw_term.match self.class.match_term_and_prefix_re
  46. 10 @match_prefix = matches[:prefix] || ""
  47. 10 @match_term = matches[:term] || ""
  48. end
  49. 1 def escape_regexp_characters
  50. 10 match_term.gsub!(/(\W)/, '\\\\\1')
  51. end
  52. end
  53. end
  54. end
  55. end

card/lib/card/reference.rb

52.94% lines covered

34 relevant lines. 18 lines covered and 16 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # a Reference is a directional relationship from one card (the referer)
  4. # to another (the referee).
  5. 1 class Reference < ApplicationRecord
  6. 1 class << self
  7. # bulk insert improves performance considerably
  8. # array takes form [ [referer_id, referee_id, referee_key, ref_type], ...]
  9. 1 def mass_insert array
  10. 179 return if array.empty?
  11. 926 value_statements = array.map { |values| "\n(#{values.join ', '})" }
  12. sql = "INSERT into card_references "\
  13. "(referer_id, referee_id, referee_key, ref_type) "\
  14. 179 "VALUES #{value_statements.join ', '}"
  15. 179 Card.connection.execute sql
  16. end
  17. # map existing reference to name to card via id
  18. 1 def map_referees referee_key, referee_id
  19. 210 where(referee_key: referee_key).update_all referee_id: referee_id
  20. end
  21. # references no longer refer to card, so remove id
  22. 1 def unmap_referees referee_id
  23. 82 where(referee_id: referee_id).update_all referee_id: nil
  24. end
  25. # find all references to missing (eg deleted) cards and reset them
  26. 1 def unmap_if_referee_missing
  27. joins(
  28. "LEFT JOIN cards ON card_references.referee_id = cards.id"
  29. ).where(
  30. "(cards.id IS NULL OR cards.trash IS TRUE) AND referee_id IS NOT NULL"
  31. ).update_all referee_id: nil
  32. end
  33. # remove all references from missing (eg deleted) cards
  34. 1 def delete_if_referer_missing
  35. joins(
  36. "LEFT JOIN cards ON card_references.referer_id = cards.id"
  37. ).where(
  38. "cards.id IS NULL"
  39. ).pluck_in_batches(:id) do |group_ids|
  40. # used to be .delete_all here, but that was failing on large dbs
  41. Rails.logger.info "deleting batch of references"
  42. where("id in (#{group_ids.join ','})").delete_all
  43. end
  44. end
  45. # repair references one by one (delete, create, delete, create...)
  46. # slower, but better than #recreate_all for use on running sites
  47. 1 def repair_all
  48. delete_if_referer_missing
  49. Card.where(trash: false).find_each do |card|
  50. Rails.logger.info "updating references from #{card}"
  51. card.include_set_modules
  52. card.update_references_out
  53. end
  54. end
  55. # delete all references, then recreate them one by one
  56. # faster than #repair_all, but not recommended for use on running sites
  57. 1 def recreate_all
  58. delete_all
  59. Card.where(trash: false).find_each do |card|
  60. Rails.logger.info "updating references from #{card}"
  61. card.include_set_modules
  62. card.create_references_out
  63. end
  64. end
  65. end
  66. # card that refers
  67. 1 def referer
  68. Card[referer_id]
  69. end
  70. # card that is referred to
  71. 1 def referee
  72. Card[referee_id]
  73. end
  74. end
  75. end

card/lib/card/set.rb

100.0% lines covered

24 relevant lines. 24 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. #
  4. # A _Set_ is a group of {Card Cards} to which _Rules_ may apply. Sets can be as
  5. # specific as a single card, as general as all cards, or anywhere in between.
  6. #
  7. # Rules can defined onto Sets in two ways:
  8. #
  9. # - **Card rules** are defined in card content. These are generally configured via the
  10. # web interface and are thus documented at https://decko.org/rules.
  11. # - **Code rules** can be defined in a 'set module'.
  12. #
  13. # The {Card::Mod} docs explain how to create mods and set_modules. This page explains
  14. # how those modules become useful.
  15. #
  16. # Suppose you have created a "mod" for managing your contacts called "contactmanager",
  17. # and it includes code that would apply to all +address cards here:
  18. #
  19. # ./contactmanager/set/right/address.rb
  20. #
  21. # Then, whenever you fetch or instantiate a +address card, the card will automatically
  22. # include code from that set module. In fact, it will include all the set modules
  23. # associated with sets of which it is a member.
  24. #
  25. # For example, say you have a Plaintext card named 'Philipp+address', and you have set
  26. # files for the following sets:
  27. #
  28. # * all cards
  29. # * all Plaintext cards
  30. # * all cards ending in +address
  31. #
  32. # When you run this:
  33. #
  34. # mycard = Card.fetch 'Philipp+address'
  35. #
  36. # ...then mycard will include the set modules associated with each of those sets in the
  37. # above order.
  38. #
  39. # Note that the set module's filename connects it to the set, so both the set_pattern
  40. # and the set_anchor must correspond to the codename of a card in the database to
  41. # function correctly.
  42. #
  43. # A set module is "just ruby", but is generally quite concise because Card uses
  44. # a) the set module's file location to autogenerate ruby module names and
  45. # b) Card::Set to provide API for the most common set methods.
  46. #
  47. 1 module Set
  48. 1 require "card/set/event"
  49. 1 require "card/set/trait"
  50. 1 require "card/set/basket"
  51. 1 require "card/set/inheritance"
  52. 1 require "card/set/format"
  53. 1 require "card/set/advanced_api"
  54. 1 require "card/set/helpers"
  55. 1 require "card/set/i18n_scope"
  56. 1 require "card/set/loader"
  57. 1 include Event::Api
  58. 1 include Trait
  59. 1 include Basket
  60. 1 include Inheritance
  61. 1 include Format
  62. 1 include AdvancedApi
  63. 1 include Helpers
  64. 1 extend I18nScope
  65. 1 extend Loader
  66. 1 mattr_accessor :modules, :traits
  67. 1 def self.reset_modules
  68. 1 self.modules = { base: [], base_format: {}, nonbase: {}, nonbase_format: {},
  69. abstract: {}, abstract_format: {} }
  70. end
  71. 1 reset_modules
  72. # SET MODULE API
  73. #
  74. # The most important parts of the set module API are views (see
  75. # Card::Set::Format) and events (see Card::Set::Event:Api)
  76. end
  77. end

card/lib/card/set/abstract.rb

100.0% lines covered

3 relevant lines. 3 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 class Abstract < Pattern::Base
  4. end
  5. end
  6. end

card/lib/card/set/advanced_api.rb

78.38% lines covered

37 relevant lines. 29 lines covered and 8 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. # advanced set module API
  4. 1 module AdvancedApi
  5. 1 def ensure_set &block
  6. 40 set_module = yield
  7. 40 set_module = card_set_module_const_get(set_module) unless set_module.is_a?(Module)
  8. 27 set_module
  9. rescue NameError => e
  10. 13 if e.message =~ /uninitialized constant (?:Card::Set::)?(.+)$/
  11. 13 define_set Regexp.last_match(1)
  12. end
  13. # try again - there might be another submodule that doesn't exist
  14. 13 ensure_set(&block)
  15. else
  16. 27 set_module.extend Card::Set
  17. end
  18. 1 def attachment name, args
  19. 2 include_set Abstract::Attachment
  20. 2 add_attributes name, "remote_#{name}_url".to_sym,
  21. :action_id_of_cached_upload, :empty_ok,
  22. :storage_type, :bucket, :mod
  23. 2 uploader_class = args[:uploader] || ::CarrierWave::FileCardUploader
  24. 2 mount_uploader name, uploader_class
  25. 2 Card.define_dirty_methods name
  26. end
  27. 1 def stage_method method, opts={}, &block
  28. class_eval do
  29. define_method "_#{method}", &block
  30. define_method method do |*args|
  31. if (error = wrong_stage(opts) || wrong_action(opts[:on]))
  32. raise Card::Error, error
  33. end
  34. send "_#{method}", *args
  35. end
  36. end
  37. end
  38. 1 private
  39. # @param set_name [String] name of the constant to be defined
  40. 1 def define_set set_name, start_const=Card::Set
  41. 13 constant_pieces = set_name.split("::")
  42. 13 constant_pieces.inject(start_const) do |set_mod, module_name|
  43. 27 set_mod.const_get_or_set module_name do
  44. 13 Module.new
  45. end
  46. end
  47. end
  48. # "set" is the noun not the verb
  49. 1 def card_set_module_const_get const
  50. 40 Card::Set.const_get normalize_const(const)
  51. end
  52. 1 def normalize_const const
  53. 40 case const
  54. when Array
  55. const.map { |piece| piece.to_s.camelcase }.join("::")
  56. when Symbol
  57. const.to_s.camelcase
  58. else
  59. 40 const
  60. end
  61. end
  62. end
  63. end
  64. end

card/lib/card/set/basket.rb

89.47% lines covered

19 relevant lines. 17 lines covered and 2 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. # The purpose of a basket it that you can throw something in from
  4. # the same set in another mod.
  5. # A basket can be defined on a format or directly on a set
  6. #
  7. # @example:
  8. # # mod/core/set/self/head.rb:
  9. # basket :basket_on_set
  10. #
  11. # format :html do
  12. # basket :js_tags # only available in HtmlFormat
  13. # view :core { output basket(:js_tags) }
  14. # end
  15. #
  16. # # mod/shell/set/self/head.rb:
  17. # add_to_basket :basket_on_set, 'hello world'
  18. #
  19. # format :html do
  20. # add_to_basket :js_tags "<script/>"
  21. # add_to_basket :js_tags do |format_obj|
  22. # format_obj.render_special_view
  23. # end
  24. # end
  25. 1 module Basket
  26. # Define a basket in a set or format
  27. 1 def basket name
  28. 9 mattr_accessor "#{name}_content"
  29. 9 send("#{name}_content=", [])
  30. 9 define_method name do
  31. 224 send("#{name}_content").map do |item|
  32. 896 item.respond_to?(:call) ? item.call(self) : item
  33. end
  34. end
  35. end
  36. # Define a basket in an abstract set
  37. 1 def abstract_basket name
  38. # the basket has to be defined on the including set
  39. # (instead on the set itself)
  40. 1 define_singleton_method :included do |host|
  41. 5 super(host)
  42. 5 host.basket name
  43. end
  44. end
  45. 1 def add_to_basket name, content=nil, &block
  46. 42 content ||= block
  47. 42 send("#{name}_content").send "<<", content
  48. end
  49. 1 def unshift_basket name, content=nil, &block
  50. content ||= block
  51. send("#{name}_content").unshift content
  52. end
  53. end
  54. end
  55. end

card/lib/card/set/event.rb

98.25% lines covered

57 relevant lines. 56 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. # Supports the definition of events via the {Api Events API}
  4. 1 class Event
  5. # Events are the building blocks of the three transformative card actions: _create_,
  6. # _update_, and _delete_.
  7. #
  8. # (The fourth kind of action, _read_, does not transform cards, and is associated
  9. # with {Card::Format views}, not events).
  10. #
  11. # As described in detail in {Card::Director}, each act can have many actions, each
  12. # action has three phases, each phase has three stages, and each stage has many
  13. # events.
  14. #
  15. # Events are defined in set modules in {Card::Mod **mods**}. Learn more about
  16. # {Card::Mod set modules}.
  17. #
  18. # A simple event definition looks something like this:
  19. #
  20. # event :append_you_know, :prepare_to_validate, on: :create do
  21. # self.content = content + ", you know?"
  22. # end
  23. #
  24. # Note:
  25. #
  26. # - `:append_you_know` is a unique event name.
  27. # - `:prepare_to_validate` is a {Card::Director stage}.
  28. # - `on: :create` specifies the action to which the event applies
  29. # - `self`, within the event card, is a card object.
  30. #
  31. # Any card within the {Card::Set set} on which this event is defined will
  32. # run this event during the `prepare_to_validate` stage when it is created.
  33. #
  34. # Events should not be defined within format blocks.
  35. 1 module Api
  36. OPTIONS = {
  37. 1 on: %i[create update delete save read],
  38. changed: Card::Dirty.dirty_options,
  39. changing: Card::Dirty.dirty_options,
  40. skip: [:allowed],
  41. trigger: [:required],
  42. when: nil
  43. }.freeze
  44. # Define an event for a set of cards.
  45. #
  46. # @param event [Symbol] unique event name
  47. # @param stage_or_opts [Symbol, Hash] if a Symbol, defines event's
  48. # {Card::Director stage}. If a Hash, it's treated as the opts param.
  49. # @param opts [Hash] event options
  50. # @option opts [Symbol, Array] :on one or more actions in which the event
  51. # should be executed. :save is shorthand for [:create, :update]. If no value
  52. # is specified, event will fire on create, update, and delete.
  53. # @option opts [Symbol, Array] :changed fire event only if field has changed.
  54. # valid values: name, content, db_content, type, type_id, left_id, right_id,
  55. # codename, trash.
  56. # @option opts [Symbol, Array] :changing alias for :changed
  57. # @option opts [Symbol] :skip allow actions to skip this event.
  58. # (eg. `skip: :allowed`)
  59. # @option opts [Symbol] :trigger allow actions to trigger this event
  60. # explicitly. If `trigger: :required`, then event will not run unless explicitly
  61. # triggered.
  62. # @option opts [Symbol, Proc] :when method (Symbol) or code (Proc) to execute
  63. # to determine whether to fire event. Proc is given card as argument.
  64. # @option opts [Symbol] :before fire this event before the event specified.
  65. # @option opts [Symbol] :after fire this event after the event specified.
  66. # @option opts [Symbol] :around fire this event before the event specified. This
  67. # event will receive a block and will need to call it for the specified
  68. # event to fire.
  69. # @option opts [Symbol] :stage alternate representation for specifying stage
  70. # @option opts [True/False] :after_subcards run event after running subcard events
  71. 1 def event event, stage_or_opts={}, opts={}, &final
  72. 191 Event.new(event, stage_or_opts, opts, self, &final).register
  73. end
  74. end
  75. 1 CONDITIONS = ::Set.new(Api::OPTIONS.keys).freeze
  76. 1 include DelayedEvent
  77. 1 include Options
  78. 1 include Callbacks
  79. 1 attr_reader :set_module, :opts
  80. 1 def initialize event, stage_or_opts, opts, set_module, &final
  81. 191 @event = event
  82. 191 @set_module = set_module
  83. 191 @opts = event_opts stage_or_opts, opts
  84. 191 @event_block = final
  85. end
  86. 1 def register
  87. 191 validate_conditions
  88. 191 Card.define_callbacks @event
  89. 191 define_event
  90. 191 set_event_callbacks
  91. end
  92. # @return the name of the event
  93. 1 def name
  94. 9248 @event
  95. end
  96. 1 def block
  97. 191 @event_block
  98. end
  99. # the name for the method that only executes the code
  100. # defined in the event
  101. 1 def simple_method_name
  102. 474 "#{@event}_without_callbacks"
  103. end
  104. # the name for the method that adds the event to
  105. # the delayed job queue
  106. 1 def delaying_method_name
  107. 6 "#{@event}_with_delay"
  108. end
  109. 1 private
  110. # EVENT DEFINITION
  111. 1 def define_event
  112. 191 define_simple_method
  113. 191 define_event_method
  114. end
  115. 1 def define_simple_method
  116. 191 @set_module.class_exec(self) do |event|
  117. 191 define_method event.simple_method_name, &event.block
  118. end
  119. end
  120. 1 def define_event_method
  121. 191 send "define_#{event_type}_event_method"
  122. end
  123. 1 def event_type
  124. 191 with_delay?(@opts) ? :delayed : :standard
  125. end
  126. 1 def define_standard_event_method method_name=simple_method_name
  127. 191 is_integration = @stage.to_s.match?(/integrate/)
  128. 191 @set_module.class_exec(@event) do |event_name|
  129. 191 define_method event_name do
  130. 6065 rescuing_if_integration is_integration do
  131. 6065 log_event_call event_name
  132. 6065 run_callbacks event_name do
  133. 6064 send method_name
  134. end
  135. end
  136. end
  137. end
  138. end
  139. end
  140. end
  141. 1 def rescuing_if_integration is_integration
  142. 6817 is_integration ? rescuing_integration { yield } : yield
  143. end
  144. # one failed integration event should not harm others.
  145. 1 def rescuing_integration
  146. 752 yield
  147. rescue StandardError => e
  148. Card::Error.report e, self
  149. ensure
  150. true
  151. 752 end
  152. 1 def log_event_call event
  153. 6065 Rails.logger.debug "#{name}: #{event}"
  154. # puts "#{name}: #{event}"
  155. # puts "#{Card::Director.to_s}".green
  156. end
  157. end

card/lib/card/set/event/callbacks.rb

93.75% lines covered

16 relevant lines. 15 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 class Event
  4. 1 module Callbacks
  5. 1 def set_event_callbacks
  6. 191 %i[before after around].each do |kind|
  7. 573 next unless (object_method = @opts.delete kind)
  8. 185 set_event_callback object_method, kind
  9. end
  10. end
  11. 1 def set_event_callback object_method, kind
  12. 185 valid_event_callback kind, object_method do
  13. 185 Card.class_exec(self) do |event|
  14. 185 set_callback object_method, kind, event.name,
  15. 46379 prepend: true, if: proc { |c| c.event_applies?(event) }
  16. end
  17. end
  18. end
  19. 1 def valid_event_callback kind, method
  20. 185 yield
  21. rescue NoMethodError
  22. raise "invalid event callback: `#{kind}: #{method}`"
  23. end
  24. end
  25. end
  26. end
  27. end

card/lib/card/set/event/delayed_event.rb

76.12% lines covered

67 relevant lines. 51 lines covered and 16 lines missed.
    
  1. 1 require "application_job"
  2. 1 class Card
  3. # attributes that ActiveJob can handle
  4. 1 def serializable_attributes
  5. 95 self.class.action_specific_attributes + set_specific.keys
  6. end
  7. 1 module Set
  8. 1 class Event
  9. 1 module DelayedEvent
  10. 1 DELAY_STAGES = ::Set.new(%i[integrate_with_delay_stage
  11. integrate_with_delay_final_stage]).freeze
  12. 1 def priority
  13. 95 @priority || 10
  14. end
  15. 1 private
  16. 1 def process_delayed_job_opts opts
  17. 191 @priority = opts.delete :priority
  18. end
  19. 1 def with_delay? opts
  20. 191 DELAY_STAGES.include?(opts[:after]) || DELAY_STAGES.include?(opts[:before])
  21. end
  22. 1 def define_delayed_event_method
  23. 3 define_event_delaying_method
  24. 3 define_standard_event_method delaying_method_name
  25. end
  26. # creates a method that creates an ActiveJob that calls the event method.
  27. # The scheduled job gets the card object as argument and all serializable
  28. # attributes of the card.
  29. # (when the job is executed ActiveJob fetches the card from the database
  30. # so all attributes get lost)
  31. # It uses the event as queue name
  32. 1 def define_event_delaying_method
  33. 3 @set_module.class_exec(self) do |event|
  34. 3 define_method(event.delaying_method_name, proc do
  35. 95 IntegrateWithDelayJob
  36. .set(set_delayed_job_args(event))
  37. .perform_later(*perform_delayed_job_args(event))
  38. end)
  39. end
  40. end
  41. 1 class IntegrateWithDelayJob < ApplicationJob
  42. 1 def perform act_id, card, card_attribs, env, auth, method_name
  43. 93 handle_perform do
  44. 93 load_card card, card_attribs
  45. 93 Director.contextualize_delayed_event act_id, card, env, auth do
  46. 93 card.send method_name
  47. end
  48. end
  49. end
  50. 1 def handle_perform
  51. 93 yield
  52. rescue StandardError => error
  53. Card::Error.report error, @card
  54. raise error
  55. ensure
  56. 93 Director.expire
  57. end
  58. 1 def load_card card, card_attribs
  59. 93 @card = card
  60. 93 Card::Cache.renew
  61. 93 card.deserialize_for_active_job! card_attribs
  62. end
  63. end
  64. end
  65. end
  66. end
  67. 1 def deserialize_for_active_job! attr
  68. 93 attr.each do |attname, val|
  69. 1599 instance_variable_set("@#{attname}", val)
  70. end
  71. 93 include_set_modules
  72. end
  73. 1 private
  74. 1 def set_delayed_job_args event
  75. 95 { queue: event.name, priority: event.priority }
  76. end
  77. 1 def perform_delayed_job_args event
  78. 95 [Card::Director.act&.id,
  79. self,
  80. serialize_for_active_job,
  81. Card::Env.serialize,
  82. Card::Auth.serialize,
  83. event.simple_method_name]
  84. end
  85. 1 def serialize_for_active_job
  86. 95 serializable_attributes.each_with_object({}) do |name, hash|
  87. 1633 hash[name] = instance_variable_get("@#{name}")
  88. end
  89. end
  90. 1 def serialize_value value
  91. # ActiveJob doesn't accept symbols and Time as arguments
  92. case value
  93. when Symbol
  94. { value: value.to_s, type: "symbol" }
  95. when Time
  96. { value: value.to_s, type: "time" }
  97. when Hash
  98. { value: serialize_hash_value(value), type: "hash" }
  99. when ActionController::Parameters
  100. serialize_value value.to_unsafe_h
  101. else
  102. { value: value }
  103. end
  104. end
  105. 1 def serialize_hash_value value
  106. value.each_with_object({}) { |(k, v), h| h[k] = serialize_value(v) }
  107. end
  108. 1 def deserialize_value val, type
  109. case type
  110. when "symbol"
  111. val.to_sym
  112. when "time"
  113. DateTime.parse val
  114. when "hash"
  115. deserialize_hash_value val
  116. else
  117. val
  118. end
  119. end
  120. 1 def deserialize_hash_value value
  121. value.each_with_object({}) do |(k, v), h|
  122. h[k] = deserialize_value v[:value], v[:type]
  123. end
  124. end
  125. end

card/lib/card/set/event/options.rb

92.86% lines covered

42 relevant lines. 39 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 class Event
  4. 1 module Options
  5. 1 def validate_conditions
  6. 191 @opts.each do |key, val|
  7. 451 next if key.in? %i[stage before after around]
  8. 266 validate_condition_name key
  9. 266 validate_condition_value key, val
  10. end
  11. end
  12. 1 def validate_condition_name condition
  13. 266 return if CONDITIONS.include? condition
  14. raise ArgumentError,
  15. "invalid condition key '#{condition}' in event '#{@event}'\n" \
  16. "valid conditions are #{CONDITIONS.to_a.join ', '}"
  17. end
  18. 1 def validate_condition_value condition, val
  19. 266 if condition == :when
  20. 64 validate_when_value val
  21. else
  22. 202 invalid = Array.wrap(val) - Api::OPTIONS[condition]
  23. 202 return if invalid.empty?
  24. raise ArgumentError,
  25. "invalid option#{'s' if invalid.size > 1} '#{invalid}' "\
  26. "for condition '#{condition}' in event '#{@event}'"
  27. end
  28. end
  29. 1 def validate_when_value val
  30. 64 return if val.is_a?(Symbol) || val.is_a?(Proc)
  31. raise ArgumentError,
  32. "invalid value for condition 'when' in event '#{@event}'\n" \
  33. "must be a symbol or a proc"
  34. end
  35. 1 def event_opts stage_or_opts, opts
  36. 191 opts = normalize_opts stage_or_opts, opts
  37. 191 process_stage_opts opts
  38. 191 process_action_opts opts
  39. 191 process_delayed_job_opts opts
  40. 191 opts
  41. end
  42. 1 def normalize_opts stage_or_opts, opts
  43. 191 if stage_or_opts.is_a? Symbol
  44. 163 opts[:stage] = stage_or_opts
  45. else
  46. 28 opts = stage_or_opts
  47. end
  48. 191 opts
  49. end
  50. 1 def process_action_opts opts
  51. 191 opts[:on] = %i[create update] if opts[:on] == :save
  52. end
  53. 1 def process_stage_opts opts
  54. 191 stage = opts.delete :stage
  55. 191 after_subcards = opts.delete :after_subcards
  56. 191 return if opts[:after] || opts[:before] || opts[:around] || !(@stage = stage)
  57. # after, before, or around will override stage configuration
  58. 159 opts[:after] = callback_name stage, after_subcards
  59. end
  60. 1 def callback_name stage, after_subcards=false
  61. 159 name = after_subcards ? "#{stage}_final_stage" : "#{stage}_stage"
  62. 159 name.to_sym
  63. end
  64. end
  65. end
  66. end
  67. end

card/lib/card/set/format.rb

97.78% lines covered

45 relevant lines. 44 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 module Set
  4. # This document explains how to use format blocks within {Card::Mod mods}. To make
  5. # use of it, you will need to understand both {Card::Mod mods} and {Card::Set sets}.
  6. #
  7. # Within a card mod, you can define format blocks like the following:
  8. #
  9. # format :html do
  10. # def beethoven
  11. # :rocks
  12. # end
  13. # end
  14. #
  15. # The magic that happens here is that the method #beethoven is now applicable (and
  16. # available) _only_ to the cards in the set specified by the mod, and only when
  17. # the card is rendering a view in the HTML format.
  18. #
  19. # If you care, you can certainly learn about how all this works. How the set module
  20. # creates a module that looks something like `Card::Set::Type::MyType::HtmlFormat`.
  21. # How the format object for a given card in the set includes this module dynamically
  22. # when it's initialized. And so on...
  23. #
  24. # But as monkeys, we don't usually think about all that much, because we work in
  25. # the set module space, which lets us focus on the card patterns.
  26. #
  27. # Speaking of which, there are a few key patterns to be aware of:
  28. #
  29. # 1. Just as in {Card::Set sets}, format methods for narrower sets will override
  30. # format methods for more general sets. So if a #beethoven method is defined
  31. # for all cards and again for a specific card type, then the type method will
  32. # override the all method when both apply.
  33. # 2. Similarly, specific formats inherit from more general formats, and all formats
  34. # inherit from the base format. If a format is not specified, the format block
  35. # will define methods on the base format class.
  36. #
  37. # format do
  38. # def haydn
  39. # :sucks
  40. # end
  41. # end
  42. #
  43. # 3. It is possible to use super to refer to overridden methods. For example
  44. #
  45. # format :html do
  46. # def haydn
  47. # "<em>#{super}</em>"
  48. # end
  49. # end
  50. #
  51. # Note: Set precedence has a higher priority than Format precedence.
  52. #
  53. # 4. {#view} and {#before} can both be called outside of a format block. They will
  54. # be defined on the base format.
  55. #
  56. # 5. Some very powerful API calls (including {AbstractFormat#view view} and
  57. # {AbstractFormat#before before}) are defined in {AbstractFormat}. These methods are
  58. # always available in format blocks.
  59. 1 module Format
  60. 1 require "card/set/format/haml_paths"
  61. 1 require "card/set/format/abstract_format"
  62. # define format behavior within a set module
  63. 1 def format *format_names, &block
  64. 17 format_names.compact!
  65. 17 if format_names.empty?
  66. 16 format_names = [:base]
  67. 1 elsif format_names.first == :all
  68. format_names =
  69. Card::Format.registered.reject { |f| Card::Format.aliases[f] }
  70. end
  71. 17 format_names.each do |f|
  72. 17 define_on_format f, &block
  73. end
  74. end
  75. # shortcut for {AbstractFormat#view} for when #view is called outside of a format
  76. # block
  77. 1 def view *args, &block
  78. 28 format { view(*args, &block) }
  79. end
  80. # shortcut for {AbstractFormat#before} for when #before is called outside of a
  81. # format block
  82. 1 def before view, &block
  83. 4 format { before view, &block }
  84. end
  85. 1 private
  86. 1 def define_on_format format_name=:base, &block
  87. # format class name, eg. HtmlFormat
  88. 374 klass = Card::Format.format_class_name format_name
  89. # called on current set module, eg Card::Set::Type::Pointer
  90. 374 mod = const_get_or_set klass do
  91. # yielding set format module, eg Card::Set::Type::Pointer::HtmlFormat
  92. 254 m = Module.new
  93. 254 register_set_format Card::Format.class_from_name(klass), m
  94. 254 m.extend Card::Set::Format::AbstractFormat
  95. 254 m
  96. end
  97. 374 mod.class_eval(&block)
  98. end
  99. 1 def register_set_format format_class, mod
  100. 531 if all_set?
  101. 109 all_set_format_mod! format_class, mod
  102. else
  103. 422 format_type = abstract_set? ? :abstract_format : :nonbase_format
  104. # ready to include dynamically in set members' format singletons
  105. 422 format_hash = modules[format_type][format_class] ||= {}
  106. 422 format_hash[shortname] ||= []
  107. 422 format_hash[shortname] << mod
  108. end
  109. end
  110. # make mod ready to include in base (non-set-specific) format classes
  111. 1 def all_set_format_mod! format_class, mod
  112. 109 modules[:base_format][format_class] ||= []
  113. 109 modules[:base_format][format_class] << mod
  114. end
  115. 1 class << self
  116. # name of method for layout
  117. # used by wrapper
  118. 1 def layout_method_name layout
  119. 4 "_layout_#{layout.to_name.key}"
  120. end
  121. # name of method for wrapper
  122. # used by wrapped views
  123. 1 def wrapper_method_name wrapper
  124. 13 "_wrapper_#{wrapper}"
  125. end
  126. # name of method for view
  127. # used by #render
  128. 1 def view_method_name view
  129. 44217 "_view_#{view}"
  130. end
  131. # name of method for setting for a given view.
  132. # used by #view_setting
  133. 1 def view_setting_method_name view, setting_name
  134. 64487 "_view_#{view}__#{setting_name}"
  135. end
  136. end
  137. end
  138. end
  139. end

card/lib/card/set/format/abstract_format.rb

92.0% lines covered

25 relevant lines. 23 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 module Set
  4. 1 module Format
  5. # AbstractFormat manages the basic format API, including API to define a {#view}.
  6. # Whenever you create a format block in a set module in a {Card::Mod mod}, you
  7. # create a format module that is extended with AbstractFormat.
  8. 1 module AbstractFormat
  9. 1 include Set::Basket
  10. 1 include ViewOpts
  11. 1 include ViewDefinition
  12. 1 include HamlViews
  13. 1 include Wrapper
  14. # _Views_ are the primary way that both sharks and monkeys interact with cards.
  15. # Sharks select views to use in _nests_. Monkeys can define and tweak those
  16. # views. These docs will introduce the basics of view definition.
  17. #
  18. # ## Sample view definitions
  19. #
  20. # Here is a very simple view that just defines a label for the card(its name):
  21. #
  22. # view :label do
  23. # card.name
  24. # end
  25. #
  26. # View definitions can take the following forms:
  27. #
  28. # view :viewname[, option_hash][, &block] # standard
  29. # view :viewname, alias_to_viewname[, option_hash] # aliasing
  30. #
  31. #
  32. # ## View definition options
  33. #
  34. # * __:alias_to__ [Symbol] name of view to which this view should be aliased. View
  35. # must already be defined in self or specified mod.
  36. #
  37. # * __:async__ render view asynchronously by first rendering a card placeholder
  38. # and then completing a request. Only applies to HtmlFormat
  39. #
  40. # * __:cache__ directs how to handle caching for this view. Supported values:
  41. # * *:standard* - (default)
  42. # * *:always* - cache even when rendered within another cached view
  43. # * *:never* - don't ever cache this view. Frequently used to prevent caching
  44. # problems
  45. #
  46. # You should certainly {Card::View::Cache learn more about caching} if you want
  47. # to develop mods that are safe in a caching environment.
  48. #
  49. # * __:compact__ [True/False]. Is view acceptable for rendering inside `compact`
  50. # view? Default is false.
  51. #
  52. # * __:denial__ [Symbol]. View to render if permission is denied. Value can be
  53. # any viewname. Default is `:denial`. `:blank` is a common alternative.
  54. #
  55. # * __:perms__ restricts view permissions. Supported values:
  56. # * *:create*, *:read* (default), *:update*, *:delete* - only users with the
  57. # given permission for the card viewed.
  58. # * *:none* - no permission check; anyone can view
  59. # * a *Proc* object. Eg `perms: ->(_fmt) { Auth.needs_setup? }`
  60. #
  61. # * __:template__ [Symbol] view is defined in a template. Currently `:haml` is
  62. # the only supported value. See {HamlViews}
  63. #
  64. # * __:unknown__ [True/False, Symbol]. Configures handling of "unknown" cards.
  65. # (See {Set::All::States card states}). Supported values:
  66. # * *true* render view even if card is unknown
  67. # * *false* default unknown handling (depends on context, create permissions,
  68. # etc)
  69. # * a *Symbol*: name of view to render
  70. #
  71. # * __:wrap__ wrap view dynamically. Value is Symbol for wrapper or Hash with
  72. # wrappers and wrapper options. See {Wrapper}
  73. #
  74. 1 def view viewname, *args, &block
  75. 521 def_opts = process_view_opts viewname, args
  76. 521 define_view_method viewname, def_opts, &block
  77. end
  78. # simple placeholder for views designed to be overridden elsewhere
  79. 1 def view_for_override viewname
  80. # LOCALIZE
  81. view viewname do
  82. "override '#{viewname}' view"
  83. end
  84. end
  85. # define code to be executed before a view is rendered
  86. 1 def before view, &block
  87. 16 define_method "_before_#{view}", &block
  88. end
  89. # Defines a setting method that can be used in all formats. Example:
  90. #
  91. # format do
  92. # setting :cols
  93. # cols 5, 7
  94. #
  95. # view :some_view do
  96. # cols # => [5, 7]
  97. # end
  98. # end
  99. #
  100. # @param name [Symbol] name of setting. should be available method name
  101. 1 def setting name
  102. 2 Card::Set::Format::AbstractFormat.send :define_method, name do |*args|
  103. 5 define_method name do
  104. 56 args
  105. end
  106. end
  107. end
  108. # file location where set mod is stored
  109. 1 def source_location
  110. 12 set_module.source_location
  111. end
  112. # @return constant for set module (without format)
  113. 1 def set_module
  114. 12 Card.const_get name.split("::")[0..-2].join("::")
  115. end
  116. end
  117. end
  118. end
  119. end

card/lib/card/set/format/abstract_format/haml_views.rb

80.0% lines covered

15 relevant lines. 12 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 module Format
  4. 1 module AbstractFormat
  5. # Support haml templates in a Rails like way:
  6. # If the view option `template: :haml` is set then a haml template is expected
  7. # in a corresponding template path and renders it.
  8. #
  9. # @example
  10. # # mod/core/set/type/basic.rb
  11. # view :my_view, template: :haml # uses mod/core/view/type/basic/my_view.haml
  12. #
  13. # view :with_instance_variables, template: :haml do
  14. # @actor = "Mark Haml"
  15. # end
  16. #
  17. # # mod/core/view/type/basic/with_instance_variables.haml
  18. # Luke is played by
  19. # = actor
  20. #
  21. # > render :with_instance_variables # => "Luke is played by Mark Haml"
  22. 1 module HamlViews
  23. 1 include Card::Set::Format::HamlPaths
  24. 1 private
  25. 1 def haml_view_block view, &block
  26. 12 path = haml_template_path view
  27. 12 haml_template_proc ::File.read(path), path, &block
  28. end
  29. 1 def haml_template_proc template, path, &block
  30. 12 proc do
  31. with_template_path path do
  32. locals = haml_block_locals(&block)
  33. haml_to_html template, locals, nil, path: path
  34. end
  35. end
  36. end
  37. end
  38. end
  39. end
  40. end
  41. end

card/lib/card/set/format/abstract_format/view_definition.rb

80.0% lines covered

35 relevant lines. 28 lines covered and 7 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 module Format
  4. 1 module AbstractFormat
  5. # handles definition of view methods
  6. 1 module ViewDefinition
  7. 1 mattr_accessor :views
  8. 182 self.views = Hash.new { |h, k| h[k] = {} }
  9. 1 private
  10. 1 def define_view_method view, def_opts, &block
  11. 521 view_block = view_block view, def_opts, &block
  12. 521 view_type = def_opts[:async] ? :async : :standard
  13. 521 send "define_#{view_type}_view_method", view, &view_block
  14. end
  15. 1 def define_standard_view_method view, &block
  16. 521 views[self][view] = block
  17. 521 define_method Card::Set::Format.view_method_name(view), &block
  18. end
  19. 1 def define_async_view_method view, &block
  20. # This case makes only sense for HtmlFormat
  21. # but I don't see an easy way to override class methods for a specific
  22. # format. All formats are extended with this general module. So
  23. # a HtmlFormat.view method would be overridden by AbstractFormat.view
  24. # We need something like AbstractHtmlFormat for that.
  25. view_content = "#{view}_async_content"
  26. define_standard_view_method view_content, &block
  27. define_standard_view_method view do
  28. %(<card-view-placeholder data-url="#{path view: view_content}" />)
  29. end
  30. end
  31. 1 def view_block view, def_opts, &block
  32. 521 if (template = def_opts[:template])
  33. 12 template_view_block view, template, &block
  34. 509 elsif (alias_to = def_opts[:alias_to])
  35. 16 alias_view_block view, alias_to, def_opts[:mod], &block
  36. else
  37. 493 block
  38. end
  39. end
  40. 1 def template_view_block view, template, &block
  41. 12 return haml_view_block(view, &block) if template == :haml
  42. raise Card::Error::ServerError, "unknown view template: #{template}"
  43. end
  44. 1 def alias_view_block view, alias_to, mod=nil
  45. 16 mod ||= self
  46. 16 if block_given?
  47. raise Card::Error::ServerError, "no blocks allowed in aliased views"
  48. end
  49. 16 views[mod][alias_to] || begin
  50. raise "cannot find #{alias_to} view in #{mod}; " \
  51. "failed to alias #{view} from #{self}"
  52. end
  53. end
  54. end
  55. end
  56. end
  57. end
  58. end

card/lib/card/set/format/abstract_format/view_opts.rb

97.06% lines covered

34 relevant lines. 33 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 module Format
  4. 1 module AbstractFormat
  5. # handles processing of view definition options
  6. # (not to be confused with view rendering options. For that, see
  7. # {Card::View::Options})
  8. 1 module ViewOpts
  9. # The unknown_ok tag is a global tag for a view and should be used only in
  10. # contexts when a Format object is not available. If a format is available,
  11. # a format-specific value can be retrieved using view settings.
  12. 1 mattr_accessor :unknown_ok
  13. 1 self.unknown_ok = {}
  14. # view setting values can be accessed from Format objects (eg within format
  15. # blocks in set modules) using #view_setting(:setting_name, :view_name)
  16. 1 VIEW_SETTINGS = %i[cache compact denial perms unknown wrap].freeze
  17. # view def opts are used in defining views but are not available
  18. # at any later point
  19. 1 VIEW_DEF_OPTS = %i[alias_to mod template async].freeze
  20. 1 private
  21. 1 def process_view_opts view, args
  22. 521 def_opts, opts = normalize_view_opts args
  23. 521 interpret_view_settings view, opts
  24. 521 fail_on_invalid_opts! view, opts
  25. 521 def_opts
  26. end
  27. 1 def fail_on_invalid_opts! view, opts
  28. 521 return unless opts.present?
  29. raise Card::Error::ServerError,
  30. "unknown view opts for #{view} view: #{opts}"
  31. end
  32. 1 def normalize_view_opts args
  33. 521 def_opts = {}
  34. 521 def_opts[:alias_to] = args.shift if args[0].is_a?(Symbol)
  35. 521 opts = args.shift || {}
  36. 521 VIEW_DEF_OPTS.each do |k|
  37. 2084 def_opts[k] ||= opts.delete k
  38. end
  39. 521 [def_opts, opts]
  40. end
  41. 1 def interpret_view_settings view, opts
  42. 521 return unless opts.present?
  43. 248 unknown_ok[view] = true if opts[:unknown] == true
  44. 248 VIEW_SETTINGS.each do |setting_name|
  45. 1488 define_view_setting_method view, setting_name, opts.delete(setting_name)
  46. end
  47. end
  48. 1 def define_view_setting_method view, setting_name, setting_value
  49. 1488 return unless setting_value
  50. 397 method_name = Card::Set::Format.view_setting_method_name view, setting_name
  51. 15117 define_method(method_name) { setting_value }
  52. end
  53. end
  54. end
  55. end
  56. end
  57. end

card/lib/card/set/format/abstract_format/wrapper.rb

96.77% lines covered

31 relevant lines. 30 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 module Format
  4. 1 module AbstractFormat
  5. # The Wrapper module provides an API to define wrap methods.
  6. # It is available in all formats.
  7. 1 module Wrapper
  8. # Defines a wrapper method with the name "wrap_with_<wrapper_name>".
  9. # @param wrapper_name [Symbol, String] the name for the wrap method
  10. #
  11. # Inside the wrap_block the variable "interior" is always available
  12. # to refer to the content that is supposed to be wrapped by the wrapper.
  13. #
  14. # Example:
  15. # wrapper :burger do |opts|
  16. # ["#{opts[:bun]}-bun", interior, "bun"].join "|"
  17. # end
  18. #
  19. # It can be used like this:
  20. # wrap_with_burger bun: "sesame" do
  21. # "meat"
  22. # end # => "sesame-bun|meat|bun"
  23. #
  24. # It's also possible to wrap a whole view with a wrapper
  25. # view :whopper, wrap: :burger do
  26. # "meat"
  27. # end
  28. #
  29. # Options for view wrappers can be provided using a hash or, if not all wrappers
  30. # need options, an array of symbols and hashes.
  31. # Example
  32. # view :big_mac, wrap: { burger: { bun: "sesame" }, paper: { color: :red } }
  33. # view :cheese_burger, wrap: [:burger, paper: { color: :yellow }]
  34. #
  35. # If you want to define a wrapper that wraps only with a single html tag
  36. # then use the following syntax:
  37. # wrapper :burger, :div, class: "medium"
  38. #
  39. # wrap_with_burger "meat" # => "<div class='medium'>meat</div>"
  40. 1 def wrapper wrapper_name, *args, &wrap_block
  41. 13 method_name = Card::Set::Format.wrapper_method_name(wrapper_name)
  42. 13 if block_given?
  43. 10 define_method method_name, &wrap_block
  44. else
  45. 3 define_tag_wrapper method_name, *args
  46. end
  47. 13 define_wrap_with_method wrapper_name, method_name
  48. end
  49. 1 def layout layout, opts={}, &block
  50. 4 Card::Layout.register_built_in_layout layout, opts
  51. 4 method_name = Card::Set::Format.layout_method_name(layout)
  52. 4 define_method method_name, &block
  53. 4 wrapper layout do
  54. send method_name
  55. end
  56. end
  57. 1 attr_accessor :interior
  58. 1 private
  59. # expects a tag with options that defines the wrap
  60. 1 def define_tag_wrapper method_name, tag_name, default_opts={}
  61. 3 class_eval do
  62. 3 define_method method_name do |opts={}|
  63. 130 add_class opts, default_opts[:class]
  64. 130 wrap_with(tag_name, interior, opts.reverse_merge(default_opts))
  65. end
  66. end
  67. end
  68. # defines the wrap_with_... method that you call to use the wrapper
  69. 1 def define_wrap_with_method wrapper_name, wrapper_method_name
  70. 13 class_exec(self) do |_format|
  71. 13 define_method "wrap_with_#{wrapper_name}" do |*args, &interior|
  72. 874 @interior, opts = interior ? [interior.call, args.first] : args
  73. 874 if method(wrapper_method_name).arity.zero?
  74. 224 send wrapper_method_name
  75. else
  76. 650 send wrapper_method_name, (opts || {})
  77. end
  78. end
  79. end
  80. end
  81. end
  82. end
  83. end
  84. end
  85. end

card/lib/card/set/format/haml_paths.rb

82.0% lines covered

50 relevant lines. 41 lines covered and 9 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 module Format
  4. # methods for handling paths to HAML templates
  5. 1 module HamlPaths
  6. 1 TEMPLATE_DIR = %w[template set].freeze
  7. 1 def haml_to_html haml, locals={}, a_binding=nil, debug_info={}
  8. 1266 a_binding ||= binding
  9. 1266 ::Haml::Engine.new(haml).render a_binding, locals || {}
  10. rescue Haml::SyntaxError => e
  11. raise Card::Error,
  12. "haml syntax error #{template_location(debug_info)}: #{e.message}"
  13. end
  14. 1 def with_template_path path
  15. 1266 old_path = @template_path
  16. 1266 @template_path = path
  17. 1266 yield
  18. ensure
  19. 1266 @template_path = old_path
  20. end
  21. 1 def haml_template_path view=nil, source=nil
  22. 1278 each_template_path(source) do |template_dir, source_dir|
  23. 1562 path = try_haml_template_path template_dir, view, source_dir
  24. 1562 return path if path
  25. end
  26. msg = "can't find haml template"
  27. msg += " for #{view}" if view.present?
  28. raise Card::Error, msg
  29. end
  30. 1 def template_location debug_info
  31. return "" unless debug_info[:path]
  32. Pathname.new(debug_info[:path]).relative_path_from(Pathname.new(Dir.pwd))
  33. end
  34. 1 def each_template_path source
  35. 1278 source = deep_source(source) || source_location
  36. 1278 basename = ::File.basename source, ".rb"
  37. 1278 source_dir = ::File.dirname source
  38. 1278 ["./#{basename}", "."].each do |template_dir|
  39. 1562 yield template_dir, source_dir
  40. end
  41. end
  42. 1 TMPSET_REGEXP = %r{(?<carddir>/card)/tmp(sets)?/set/mod\d{3}-(?<modname>[^/]+)/}
  43. 1 def deep_source source
  44. 1278 return source unless Cardio.config.load_strategy == :tmp_files
  45. 1278 source&.gsub TMPSET_REGEXP do
  46. 1266 match = Regexp.last_match
  47. 1266 source_mod_dir match[:modname], match[:carddir]
  48. end
  49. end
  50. 1 def source_mod_dir modname, carddir
  51. 1266 prefix = "#{carddir}/mod" unless modname.match?(/^card-mod-/)
  52. 1266 "#{prefix}/#{modname}/set/"
  53. end
  54. 1 def try_haml_template_path template_path, view, source_dir, ext="haml"
  55. 1562 template_path = File.join(template_path, view.to_s) if view.present?
  56. 1562 template_path += ".#{ext}"
  57. 1562 TEMPLATE_DIR.each do |template_dir|
  58. 2806 path = ::File.expand_path(template_path, source_dir)
  59. .sub(%r{(/mod/[^/]+)/set/}, "\\1/#{template_dir}/")
  60. 2806 return path if ::File.exist?(path)
  61. end
  62. 284 false
  63. end
  64. 1 def haml_block_locals &block
  65. instance_exec(&block) if block_given?
  66. instance_variables.each_with_object({}) do |var, h|
  67. h[var.to_s.tr("@", "").to_sym] = instance_variable_get var
  68. end
  69. end
  70. end
  71. end
  72. end
  73. end

card/lib/card/set/helpers.rb

95.65% lines covered

23 relevant lines. 22 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 module Helpers
  4. 1 SET_PATTERN_TEST_REGEXP = /^(?<pattern>\w+)_set\?$/
  5. 1 def shortname
  6. 2675 first = 2 # shortname eliminates Card::Set
  7. 2675 last = first + num_set_parts(pattern_code)
  8. 2675 set_name_parts[first..last].join "::"
  9. end
  10. 1 def underscore
  11. 4 shortname.tr(":", "_").underscore
  12. end
  13. 1 def num_set_parts pattern_code
  14. 2675 return 1 if pattern_code == :abstract
  15. 1261 Pattern.find(pattern_code).anchor_parts_count
  16. end
  17. 1 def set_name_parts
  18. 3096 @set_name_parts ||= name.split "::"
  19. end
  20. 1 def pattern_code
  21. 4652 @pattern_code ||= set_name_parts[2].underscore.to_sym
  22. end
  23. # handles all_set?, abstract_set?, type_set?, etc.
  24. 1 def method_missing method_name, *args
  25. 1977 if (matches = method_name.match SET_PATTERN_TEST_REGEXP)
  26. 1977 pattern_code == matches[:pattern].to_sym
  27. else
  28. super
  29. end
  30. end
  31. 1 def respond_to_missing? method_name, _include_private=false
  32. 34482 method_name.match? SET_PATTERN_TEST_REGEXP
  33. end
  34. end
  35. end
  36. end

card/lib/card/set/i18n_scope.rb

90.0% lines covered

40 relevant lines. 36 lines covered and 4 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 module I18nScope
  4. # return scope for I18n
  5. 1 def scope backtrace
  6. 43 parts = path_parts backtrace
  7. 43 index = path_set_index parts
  8. 43 mod = mod_from_parts parts, index
  9. 43 set = set_from_parts parts, index
  10. 43 "mod.#{mod}.set.#{set}"
  11. end
  12. # extract the mod name from the path of a set's tmp file
  13. 1 def mod_name backtrace
  14. 35 parts = path_parts backtrace
  15. 35 mod_from_parts parts, path_set_index(parts)
  16. end
  17. 1 private
  18. 1 def set_from_parts parts, index
  19. 43 start_index = index + (tmp_files? ? 2 : 1)
  20. 43 parts[start_index..-1].join "."
  21. end
  22. 1 def mod_from_parts parts, set_index
  23. 78 if tmp_files?
  24. 78 mod_from_tmp_parts parts, set_index
  25. else
  26. parts[set_index - 1]
  27. end
  28. end
  29. 1 def mod_from_tmp_parts parts, set_index
  30. 78 parts[set_index + 1].gsub(/^[^-]*\-/, "")
  31. end
  32. 1 def path_parts backtrace
  33. 78 parts = find_set_path(backtrace).split(File::SEPARATOR)
  34. 78 parts[-1] = parts.last.split(".").first
  35. 78 parts
  36. end
  37. # extract mod and set from real path
  38. # @example
  39. # if the path looks like ~/mydeck/mod/core/set/all/event.rb/
  40. # this method returns ["core", "all", "event"]
  41. # def set_path_parts backtrace
  42. # parts = path_parts backtrace
  43. # res = parts[path_mod_index(parts)..-1]
  44. # res.delete_at 1
  45. # end
  46. # extract mod and set from tmp path
  47. # @example
  48. # a tmp path looks like ~/mydeck/tmp/set/mod002-core/all/event.rb/
  49. # this method returns ["core", "all", "event"]
  50. # def tmp_set_path_parts backtrace
  51. # path_parts = find_tmp_set_path(backtrace).split(File::SEPARATOR)
  52. # res = path_parts[tmp_path_mod_index(path_parts)..-1]
  53. # res[0] = mod_name_from_tmp_dir res.first
  54. # res[-1] = res.last.split(".").first
  55. # res
  56. # end
  57. #
  58. # def find_tmp_set_path backtrace
  59. # path = backtrace.find { |line| line.include? "tmp/set/" }
  60. # raise Error, "couldn't find set path in backtrace: #{backtrace}" unless path
  61. #
  62. # path
  63. # end
  64. #
  65. #
  66. 1 def tmp_files?
  67. 121 Card.config.load_strategy == :tmp_files
  68. end
  69. 1 def find_set_path backtrace
  70. 78 re = %r{(?<!card)/set/}
  71. 331 path = backtrace.find { |line| line =~ re }
  72. 78 raise Error, "couldn't find set path in backtrace: #{backtrace}" unless path
  73. 78 path
  74. end
  75. # # index of the mod part in the tmp path
  76. # def tmp_path_mod_index parts
  77. # unless (set_index = parts.index("set")) &&
  78. # parts.size >= set_index + 2
  79. # raise Error, "not a valid set path: #{path}"
  80. # end
  81. #
  82. # set_index + 1
  83. # end
  84. 1 def mod_name_from_tmp_dir dir
  85. match = dir.match(/^mod\d+-(?<mod_name>.+)$/)
  86. match[:mod_name]
  87. end
  88. # index of the mod part in the path
  89. 1 def path_set_index parts
  90. 78 unless (set_index = parts.index("set")) &&
  91. parts.size >= set_index + 2
  92. raise Error, "not a valid set path: #{path}"
  93. end
  94. 78 set_index
  95. end
  96. end
  97. end
  98. end

card/lib/card/set/inheritance.rb

93.1% lines covered

29 relevant lines. 27 lines covered and 2 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. # API to inherit other sets and their formats
  4. 1 module Inheritance
  5. # include a set module and all its format modules
  6. # @param [Module] set
  7. # @param [Hash] opts choose the formats you want to include. You can also
  8. # pass arbitrary options to the included set. The option is saved
  9. # in the including set. To use the option you need a `included` method
  10. # in the included set to fetch the option.
  11. # @option opts [Symbol, Array<Symbol>] :only include only these formats
  12. # @option opts [Symbol, Array<Symbol>] :except don't include these formats
  13. # @example
  14. # include_set Type::Basic, except: :css
  15. # @example pass an option
  16. # include_set Type::Name, default_name: "Luke"
  17. # default_name # => "Luke"
  18. #
  19. # def introduce_yourself
  20. # puts my_name_is # => "Luke"
  21. # end
  22. #
  23. # # in Type::Name
  24. # def self.included host_class
  25. # host_class.class_eval do
  26. # define_method :my_name_is do |name=nil|
  27. # name || host_class.default_name
  28. # end
  29. # end
  30. # end
  31. #
  32. 1 def include_set set, opts={}
  33. 128 opts.each do |key, value|
  34. cattr_accessor key
  35. send "#{key}=", value
  36. end
  37. 128 set_type = set.abstract_set? ? :abstract : :nonbase
  38. 128 add_set_modules Card::Set.modules[set_type][set.shortname]
  39. 128 include_set_formats set, opts
  40. end
  41. # include format modules of a set
  42. # @param [Module] set
  43. # @param [Hash] opts choose the formats you want to include
  44. # @option opts [Symbol, Array<Symbol>] :only include only these formats
  45. # @option opts [Symbol, Array<Symbol>] :except don't include these formats
  46. # @example
  47. # include_set_formats Type::Basic, except: :css
  48. 1 def include_set_formats set, opts={}
  49. 128 each_format set do |format, format_mods|
  50. 257 format_sym = Card::Format.format_sym format
  51. 257 next unless applicable_format?(format_sym, opts[:except], opts[:only])
  52. 257 format_mods.each do |format_mod|
  53. 357 define_on_format format_sym do
  54. 357 include format_mod
  55. end
  56. end
  57. end
  58. end
  59. 1 private
  60. # iterate through each format associated with a set
  61. 1 def each_format set
  62. 128 set_type = set.abstract_set? ? :abstract : :nonbase
  63. 128 format_type = "#{set_type}_format".to_sym
  64. 128 modules[format_type].each_pair do |format, set_format_mod_hash|
  65. 1069 next unless (format_mods = set_format_mod_hash[set.shortname])
  66. 257 yield format, format_mods
  67. end
  68. end
  69. 1 def applicable_format? format, except, only
  70. 257 format_sym = Card::Format.format_sym format
  71. 257 return false if except && Array(except).include?(format_sym)
  72. 257 return false if only && !Array(only).include?(format_sym)
  73. 257 true
  74. end
  75. end
  76. end
  77. end

card/lib/card/set/loader.rb

100.0% lines covered

26 relevant lines. 26 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 module Set
  4. # the set loading process has two main phases:
  5. # 1. Definition: interpret each set file, creating/defining set and
  6. # set_format modules
  7. # 2. Organization: have base classes include modules associated with the
  8. # 'all' set, and clean up the other modules
  9. 1 module Loader
  10. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  11. # Definition Phase
  12. # each set file calls `extend Card::Set` when loaded
  13. 1 def extended mod
  14. 450 register_set mod
  15. end
  16. # make the set available for use
  17. 1 def register_set set_module
  18. 451 if set_module.all_set?
  19. # automatically included in Card class
  20. 136 modules[:base] << set_module
  21. else
  22. 315 set_type = set_module.abstract_set? ? :abstract : :nonbase
  23. # made ready for dynamic loading via #include_set_modules
  24. 315 modules[set_type][set_module.shortname] ||= []
  25. 315 modules[set_type][set_module.shortname] << set_module
  26. end
  27. end
  28. #
  29. # When a Card application loads, it uses set modules to autogenerate
  30. # tmp files that add module names (Card::Set::PATTERN::ANCHOR) and
  31. # extend the module with Card::Set.
  32. #
  33. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  34. # Organization Phase
  35. # 'base modules' are modules that are always included on the Card or
  36. # Format class
  37. # 'nonbase modules' are included dynamically on singleton_classes
  38. 1 def process_base_modules
  39. 1 return unless modules[:base].present?
  40. 1 Card.add_set_modules modules[:base]
  41. 1 modules[:base_format].each do |format_class, modules_list|
  42. 11 format_class.add_set_modules modules_list
  43. end
  44. 1 modules[:base].clear
  45. 1 modules[:base_format].clear
  46. end
  47. 1 def clean_empty_modules
  48. 1 clean_empty_module_from_hash modules[:nonbase]
  49. 1 modules[:nonbase_format].values.each do |hash|
  50. 11 clean_empty_module_from_hash hash
  51. end
  52. end
  53. 1 def clean_empty_module_from_hash hash
  54. 12 hash.each do |mod_name, modlist|
  55. 1089 modlist.delete_if { |x| x.instance_methods.empty? }
  56. 493 hash.delete mod_name if modlist.empty?
  57. end
  58. end
  59. end
  60. end
  61. end

card/lib/card/set/pattern.rb

48.15% lines covered

27 relevant lines. 13 lines covered and 14 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 class Pattern
  4. 1 class << self
  5. 1 def reset
  6. nonbase_loadables.each do |set_pattern|
  7. Card::Set.const_remove_if_defined set_pattern.to_s.split("::").last
  8. end
  9. Card.set_patterns = []
  10. @card_keys = nil
  11. end
  12. 1 def loadables
  13. Card.set_patterns.push(Card::Set::Abstract).reverse
  14. end
  15. 1 def nonbase_loadables
  16. l = loadables
  17. l.delete Card::Set::All
  18. l
  19. end
  20. 1 def find pattern_code
  21. 8280 Card.set_patterns.find { |sub| sub.pattern_code == pattern_code }
  22. end
  23. 1 def card_keys
  24. @card_keys ||=
  25. Card.set_patterns.each_with_object({}) do |set_pattern, hash|
  26. card_key = Card.quick_fetch(set_pattern.pattern_code).key
  27. hash[card_key] = true
  28. end
  29. end
  30. 1 def nonbase_loadable_codes
  31. l = loadable_codes
  32. l.delete :all
  33. l
  34. end
  35. 1 def loadable_codes
  36. 29 Card.set_patterns.map(&:pattern_code).push(:abstract).reverse
  37. end
  38. end
  39. end
  40. end
  41. end

card/lib/card/set/pattern/base.rb

95.24% lines covered

84 relevant lines. 80 lines covered and 4 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 class Pattern
  4. # class from which set patterns inherit
  5. 1 class Base
  6. 1 class << self
  7. 1 attr_accessor :pattern_code, :pattern_id, :junction_only,
  8. :assigns_type, :anchorless
  9. 1 attr_writer :anchor_parts_count
  10. 1 def new card
  11. 69066 super if pattern_applies? card
  12. end
  13. 1 def register pattern_code, opts={}
  14. 9 if (self.pattern_id = Card::Codename.id(pattern_code))
  15. 9 self.pattern_code = pattern_code
  16. 9 Card.set_patterns.insert opts.delete(:index).to_i, self
  17. 9 self.anchorless = !respond_to?(:anchor_name)
  18. 17 opts.each { |key, val| send "#{key}=", val }
  19. else
  20. warn "no codename for pattern_code #{pattern_code}"
  21. end
  22. end
  23. 1 def junction_only?
  24. 38370 junction_only == true
  25. end
  26. 1 def anchorless?
  27. 147449 anchorless
  28. end
  29. 1 def pattern
  30. 954 Card.fetch(pattern_id, skip_modules: true).name
  31. end
  32. 1 def pattern_applies? card
  33. 38370 junction_only? ? card.name.junction? : true
  34. end
  35. 1 def anchor_parts_count
  36. 60579 @anchor_parts_count ||= (anchorless? ? 0 : 1)
  37. end
  38. 1 def module_key anchor_codes
  39. 52905 return pattern_code.to_s.camelize if anchorless?
  40. 34147 return unless anchor_codes # is this not an error?
  41. 80932 ([pattern_code] + anchor_codes).map { |code| code.to_s.camelize }.join "::"
  42. end
  43. # label for set pattern if no anchor is given
  44. 1 def generic_label
  45. label nil
  46. end
  47. end
  48. # Instance methods
  49. 1 def initialize card
  50. 40353 return if self.class.anchorless?
  51. 22845 @anchor_name = self.class.anchor_name(card).to_name
  52. 22845 @anchor_id = find_anchor_id card
  53. end
  54. 1 def find_anchor_id card
  55. 22845 self.class.try(:anchor_id, card) || Card.fetch_id(@anchor_name)
  56. end
  57. 1 def module_key
  58. 186560 return @module_key if defined? @module_key
  59. 52905 @module_key = self.class.module_key anchor_codenames
  60. end
  61. 1 def lookup_module_list modules_hash
  62. 83445 module_key && modules_hash[module_key]
  63. end
  64. 1 def module_list
  65. 62144 lookup_module_list Card::Set.modules[:nonbase]
  66. end
  67. 1 def format_module_list klass
  68. 52132 hash = Card::Set.modules[:nonbase_format][klass]
  69. 52132 hash && lookup_module_list(hash)
  70. end
  71. 1 def anchor_codenames
  72. 52905 anchor_parts.map do |part|
  73. 58653 part_id = Card.fetch_id part
  74. 58653 Card::Codename[part_id] || break
  75. end
  76. end
  77. 1 def anchor_parts
  78. 52905 return [@anchor_name] unless anchor_parts_count > 1
  79. 6042 parts = @anchor_name.parts
  80. 6042 if parts.size <= anchor_parts_count
  81. 6042 parts
  82. else
  83. # handles cases where anchor is a compound card, eg A+B+*self
  84. [@anchor_name[0..-anchor_parts_count]] + parts[(-anchor_parts_count + 1)..-1]
  85. end
  86. end
  87. 1 def anchor_parts_count
  88. 58947 self.class.anchor_parts_count
  89. end
  90. 1 def pattern
  91. 954 @pattern ||= self.class.pattern
  92. end
  93. 1 def to_s
  94. 954 self.class.anchorless? ? pattern.s : "#{@anchor_name}+#{pattern}"
  95. end
  96. 1 def inspect
  97. "<#{self.class} #{to_s.to_name.inspect}>"
  98. end
  99. 1 def safe_key
  100. 7223 caps_part = self.class.pattern_code.to_s.tr(" ", "_").upcase
  101. 7223 self.class.anchorless? ? caps_part : "#{caps_part}-#{@anchor_name.safe_key}"
  102. end
  103. 1 def rule_set_key
  104. 46006 if self.class.anchorless?
  105. 21792 self.class.pattern_code.to_s
  106. 24214 elsif @anchor_id
  107. 18887 "#{@anchor_id}+#{self.class.pattern_code}"
  108. end
  109. end
  110. end
  111. 1 module Helper
  112. 1 private
  113. 1 def left_type card
  114. 4380 card.superleft&.type_name || quick_type(card.name.left_name)
  115. end
  116. 1 def quick_type name
  117. 4042 if name.present?
  118. 4014 card = Card.fetch name, skip_modules: true, new: {}
  119. 4014 card.include_set_modules if card.new? && name.to_name.junction?
  120. 4014 card&.type_name
  121. else
  122. 28 "RichText"
  123. end
  124. end
  125. end
  126. end
  127. end
  128. end

card/lib/card/set/required_field.rb

85.0% lines covered

60 relevant lines. 51 lines covered and 9 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 class RequiredField
  4. 1 attr_reader :parent_set, :field, :options
  5. 1 def initialize parent_set, field, options={}
  6. 2 @parent_set = parent_set
  7. 2 @field = field
  8. 2 @options = options
  9. end
  10. 1 def add
  11. 2 create_parent_event
  12. 2 return unless field_events?
  13. 1 define_field_test
  14. 1 create_field_events
  15. end
  16. 1 def parent_event_name
  17. 2 [parent_set.underscore, "requires_field", field].join("__").to_sym
  18. end
  19. 1 def field_event_name action
  20. 2 [field, "required_by", parent_set.underscore, "on", action].join("__").to_sym
  21. end
  22. 1 private
  23. 1 def define_field_test
  24. 1 return unless (test = event_test)
  25. method_name = field_test_name
  26. field_set.class_exec do
  27. define_method method_name do
  28. left.send test
  29. end
  30. end
  31. end
  32. 1 def field_test_name
  33. return unless event_test
  34. "_when_left_#{event_test}".to_sym
  35. end
  36. 1 def event_test
  37. 3 return @event_test unless @event_test.nil?
  38. 1 test = options[:when]
  39. 1 @event_test = test&.is_a?(Symbol) ? test : false
  40. end
  41. 1 def field_set
  42. 2 @field_set ||= ensure_field_set parent_set, field
  43. end
  44. # for now, we only support field events on type sets. That's because only type sets
  45. # have fields that are set-addressable (via type plus right sets)
  46. 1 def field_events?
  47. 2 parent_set.type_set?
  48. end
  49. 1 def create_field_events
  50. 1 create_field_event :delete, "deleted", :trashed_left?
  51. 1 create_field_event :update, "renamed", :same_field?, changing: :name
  52. end
  53. 1 def field_event_options action, extra_options
  54. 2 options = { on: action }.merge extra_options
  55. 2 options[:when] = field_test_name if event_test
  56. 2 options
  57. end
  58. 1 def create_field_event action, action_verb, allow_test, extra_options={}
  59. 2 event_name = field_event_name action
  60. 2 event_options = field_event_options action, extra_options
  61. 2 field_set.class_exec(self) do |required|
  62. 2 event event_name, :validate, event_options do
  63. return if send allow_test
  64. errors.add required.field, "can't be #{action_verb}; required field"
  65. end
  66. end
  67. end
  68. 1 def create_parent_event
  69. 2 parent_set.class_exec(self) do |required|
  70. 2 event required.parent_event_name, :validate,
  71. required.options.merge(on: :create) do
  72. 3 return if field?(required.field) || left&.type_id == Card::CardtypeID
  73. # Without the Cardtype exemption, we can get errors on type plus right sets
  74. # eg, if right/account has require_field :email, then when we're trying
  75. # to create User+*account+*type_plus right rules, it fails, because
  76. # User+*account doesn't have an +email field.
  77. #
  78. # Need a better solution so we can require fields on cardtype+X cards, too.
  79. errors.add required.field, "required" # LOCALIZE
  80. end
  81. end
  82. end
  83. 1 def ensure_field_set parent_set, field
  84. 4 field_set = parent_set.ensure_set { field_set_name parent_set, field }
  85. 1 Card::Set.register_set field_set
  86. 1 field_set
  87. end
  88. 1 def field_set_name parent_set, field
  89. 3 "TypePlusRight::#{parent_set.set_name_parts.last}::#{field.to_s.capitalize}"
  90. end
  91. end
  92. end
  93. end

card/lib/card/set/trait.rb

88.89% lines covered

45 relevant lines. 40 lines covered and 5 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. # ActiveCard support: accessing plus cards as attributes
  4. 1 module Trait
  5. 1 def card_accessor *args
  6. 11 options = args.extract_options!
  7. 11 add_traits args, options.merge(reader: true, writer: true)
  8. end
  9. 1 def card_reader *args
  10. 3 options = args.extract_options!
  11. 3 add_traits args, options.merge(reader: true)
  12. end
  13. 1 def card_writer *args
  14. options = args.extract_options!
  15. add_traits args, options.merge(writer: true)
  16. end
  17. 1 def require_field *fields
  18. 2 options = fields.last.is_a?(Hash) ? fields.pop : {}
  19. 2 fields.each do |field|
  20. 2 Card::Set::RequiredField.new(self, field, options).add
  21. end
  22. end
  23. 1 private
  24. 1 def add_attributes *args
  25. 2 Card.set_specific_attributes ||= []
  26. 2 Card.set_specific_attributes += args.map(&:to_s)
  27. 2 Card.set_specific_attributes.uniq!
  28. end
  29. 1 def get_traits mod
  30. 14 Card::Set.traits ||= {}
  31. 14 Card::Set.traits[mod] || Card::Set.traits[mod] = {}
  32. end
  33. 1 def add_traits args, options
  34. 14 mod = self
  35. 14 mod_traits = get_traits mod
  36. 14 new_opts = options[:type] ? { type: options[:type] } : {}
  37. 14 new_opts[:default_content] = options[:default] if options[:default]
  38. 14 args.each do |trait|
  39. 14 define_trait_card trait, new_opts
  40. 14 define_trait_reader trait if options[:reader]
  41. 14 define_trait_writer trait if options[:writer]
  42. 14 mod_traits[trait.to_sym] = options
  43. end
  44. end
  45. 1 def define_trait_card trait, opts
  46. 14 define_method "#{trait}_card" do
  47. 1256 fetch trait.to_sym, new: opts.clone, eager_cache: true
  48. end
  49. end
  50. 1 def define_trait_reader trait
  51. 14 define_method trait do
  52. 358 send("#{trait}_card").content
  53. end
  54. end
  55. 1 def define_trait_writer trait
  56. 11 define_method "#{trait}=" do |value|
  57. card = send "#{trait}_card"
  58. subcards.add name: card.name, type_id: card.type_id, content: value
  59. instance_variable_set "@#{trait}", value
  60. end
  61. end
  62. end
  63. end
  64. end

card/lib/card/set/type.rb

100.0% lines covered

26 relevant lines. 26 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Set
  3. 1 class Type < Pattern::Base
  4. 1 def initialize card
  5. 6411 super
  6. # support type inheritance
  7. 6411 @inherit_card = card unless module_key
  8. end
  9. 1 def lookup_module_list modules_hash
  10. 29843 lookup_key = module_key || inherited_key
  11. 29843 modules_hash[lookup_key] if lookup_key
  12. end
  13. 1 private
  14. 1 def inherited_key
  15. 702 if defined?(@inherited_key)
  16. 424 @inherited_key
  17. else
  18. 278 @inherited_key = lookup_inherited_key
  19. end
  20. end
  21. 1 def lookup_inherited_key
  22. 278 return unless (card = @inherit_card)
  23. 278 @inherit_card = nil
  24. 278 return unless (type_code = default_type_code card)
  25. 278 mod_key = "Type::#{type_code.to_s.camelize}"
  26. 278 mod_key if mods_exist_for_key? mod_key
  27. end
  28. 1 def default_type_code card
  29. 278 card.rule_card(:default)&.type_code
  30. end
  31. 1 def mods_exist_for_key? mod_key
  32. 278 list_of_hashes = Card::Set.modules[:nonbase_format].values
  33. 278 list_of_hashes << Card::Set.modules[:nonbase]
  34. 556 list_of_hashes.any? { |h| h[mod_key] }
  35. end
  36. end
  37. end
  38. end

card/lib/card/subcards.rb

78.57% lines covered

56 relevant lines. 44 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # API to create/update/delete additional cards together with the main card.
  4. # The most common case is for fields but subcards don't have to be descendants.
  5. #
  6. # Subcards can be added as card objects or attribute hashes.
  7. #
  8. # Use the methods defined in core/set/all/subcards.rb
  9. # Example
  10. # Together with "my address" you want to create the subcards
  11. # "my address+name", "my address+street", etc.
  12. 1 class Subcards
  13. 1 include Add
  14. 1 include Remove
  15. 1 include Relate
  16. 1 attr_accessor :context_card, :keys
  17. 1 def initialize context_card
  18. 545 @context_card = context_card
  19. 545 @keys = ::Set.new
  20. end
  21. 1 def [] name
  22. card(name) || field(name)
  23. end
  24. 1 def field name
  25. 147 key = field_name_to_key name
  26. 147 fetch_subcard key if @keys.include? key
  27. end
  28. 1 def card name
  29. return unless @keys.include? name.to_name.key
  30. fetch_subcard name
  31. end
  32. 1 def present?
  33. 27 @keys.present?
  34. end
  35. 1 def catch_up_to_stage stage_index
  36. each_card do |subcard|
  37. subcard.catch_up_to_stage stage_index
  38. end
  39. end
  40. 1 def rename old_name, new_name
  41. 88 return unless @keys.include? old_name.to_name.key
  42. @keys.delete old_name.to_name.key
  43. @keys << new_name.to_name.key
  44. end
  45. 1 def respond_to_missing? method_name, _include_private=false
  46. @keys.respond_to? method_name
  47. end
  48. 1 def method_missing method, *args
  49. return unless respond_to_missing?(method)
  50. @keys.send method, *args
  51. end
  52. # fetch all cards first to avoid side effects
  53. # e.g. deleting a user adds follow rules and +*account to subcards
  54. # for deleting but deleting follow rules can remove +*account from the
  55. # cache if it belongs to the rule cards
  56. 1 def cards
  57. 3421 @keys.map do |key|
  58. 1330 fetch_subcard key
  59. end.compact
  60. end
  61. 1 def each_card
  62. 3421 cards.each do |card|
  63. 1330 yield card
  64. end
  65. end
  66. 1 alias_method :each, :each_card
  67. 1 def each_with_key
  68. 451 @keys.each do |key|
  69. 201 card = fetch_subcard(key)
  70. 201 yield(card, key) if card
  71. end
  72. end
  73. 1 def fetch_subcard key
  74. 1702 Card.fetch key, local_only: true, new: {}
  75. end
  76. 1 private
  77. 1 def subcard_key cardish
  78. 24 key = case cardish
  79. when Card then cardish.key
  80. when Symbol then fetch_subcard(cardish).key
  81. 24 else cardish.to_name.key
  82. end
  83. 24 key = absolutize_subcard_name(key).key unless @keys.include?(key)
  84. 24 key
  85. end
  86. 1 def absolutize_subcard_name name
  87. 253 name = Card::Name[name]
  88. 253 return name if @context_card.name.parts.first.blank?
  89. 253 name.absolute_name @context_card.name
  90. end
  91. end
  92. end

card/lib/card/subcards/add.rb

80.0% lines covered

60 relevant lines. 48 lines covered and 12 lines missed.
    
  1. 1 class Card
  2. 1 class Subcards
  3. # Methods for adding subcards
  4. 1 module Add
  5. # @example Add a subcard with name 'spoiler'
  6. # add 'spoiler', type: 'Phrase', content: 'John Snow is a Targaryen'
  7. # card_obj = Card.new name: 'spoiler', type: 'Phrase',
  8. # content: 'John Snow is a Targaryen'
  9. # add card_obj
  10. # add name: 'spoiler', type: 'Phrase', content: 'John Snow is a Targaryen'
  11. #
  12. # @example Add a subcard that is added in the integration phase
  13. # (and hence doesn't hold up the transaction for the main card)
  14. # add 'spoiler', content: 'John Snow is a Targaryen'
  15. # add card_obj, delayed: true
  16. 1 def << value
  17. add value
  18. end
  19. 1 def []= name, card_or_attr
  20. case card_or_attr
  21. when Hash
  22. new_by_attributes name, card_or_attr
  23. when Card
  24. new_by_card card_or_attr
  25. end
  26. end
  27. 1 def add *args
  28. 496 case args.first
  29. 87 when Card then new_by_card args.first
  30. 357 when Hash then add_hash args.first
  31. 52 else new_by_attributes(*args)
  32. end
  33. end
  34. 1 def add_hash hash
  35. 357 if (name = hash.delete :name)
  36. new_by_attributes name, hash
  37. else
  38. 357 multi_add hash
  39. end
  40. end
  41. 1 def add_child name, args
  42. 7 name = name.is_a?(Symbol) ? name.cardname : name.to_name
  43. 7 add name.prepend_joint, args
  44. end
  45. 1 alias_method :add_field, :add_child
  46. 1 def new_by_card card
  47. 340 card.supercard = @context_card
  48. 340 if !card.name.simple? && card.name.field_of?(@context_card.name)
  49. 211 card.superleft = @context_card
  50. end
  51. 340 @keys << card.key
  52. 340 Card.write_to_soft_cache card
  53. 340 card.director = @context_card.director.subdirectors.add card
  54. 340 card
  55. end
  56. 1 def new_by_attributes name, attributes={}
  57. 253 attributes ||= {}
  58. 253 absolute_name = absolutize_subcard_name name
  59. 253 subcard_args = extract_subcard_args! attributes
  60. 253 card = initialize_by_attributes absolute_name, attributes
  61. 253 subcard = new_by_card card
  62. 253 card.subcards.add subcard_args
  63. 253 subcard
  64. end
  65. 1 def initialize_by_attributes name, attributes
  66. 253 Card.assign_or_newish name, attributes, local_only: true
  67. end
  68. # TODO: this method already exists as card instance method in
  69. # tracked_attributes.rb. Find a place for it where its accessible
  70. # for both. There is one important difference. The keys are symbols
  71. # here instead of strings
  72. 1 def extract_subcard_args! args
  73. 253 subcards = args.delete(:subcards) || {}
  74. 253 if (subfields = args.delete(:subfields))
  75. subfields.each_pair do |key, value|
  76. subcards[normalize_subfield_key(key)] = value
  77. end
  78. end
  79. 253 args.keys.each do |key|
  80. 270 subcards[key] = args.delete(key) if key =~ /^\+/
  81. end
  82. 253 subcards
  83. end
  84. 1 private
  85. # ensure a leading '+'
  86. 1 def normalize_subfield_key key
  87. key = Card::Codename.name(key) if key.is_a?(Symbol) && Card::Codename.exist?(key)
  88. key.to_name.prepend_joint
  89. end
  90. # Handles hash with several subcards
  91. 1 def multi_add args
  92. 357 args.each_pair do |key, val|
  93. 201 case val
  94. when String, Array, Integer
  95. 2 new_by_attributes key, content: val
  96. when Card
  97. val.name = absolutize_subcard_name key
  98. new_by_card val
  99. when nil
  100. next
  101. else
  102. 199 new_by_attributes key, val
  103. end
  104. end
  105. end
  106. end
  107. end
  108. end

card/lib/card/subcards/relate.rb

78.57% lines covered

14 relevant lines. 11 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 class Subcards
  3. # Methods for handling related subcards
  4. 1 module Relate
  5. 1 def field_name_to_key name
  6. 147 if @context_card.name.starts_with_joint?
  7. relative_child(name).key
  8. else
  9. 147 child(name).key
  10. end
  11. end
  12. 1 def child name
  13. 147 absolute_name = @context_card.name.field_name name
  14. 147 if @keys.include? absolute_name.key
  15. 147 absolute_name
  16. else
  17. relative_child name
  18. end
  19. end
  20. 1 def relative_child name
  21. @context_card.name.relative_field_name name
  22. end
  23. end
  24. end
  25. end

card/lib/card/subcards/remove.rb

66.67% lines covered

27 relevant lines. 18 lines covered and 9 lines missed.
    
  1. 1 class Card
  2. 1 class Subcards
  3. # Methods for removing/clearing subcards
  4. 1 module Remove
  5. 1 def remove_child cardish
  6. child = cardish.is_a?(Card) ? cardish : child(cardish)
  7. remove child
  8. end
  9. 1 alias_method :remove_field, :remove_child
  10. 1 def remove name_or_card
  11. 24 key = subcard_key name_or_card
  12. 24 return unless @keys.include? key
  13. 24 @keys.delete key
  14. 24 clear_key key
  15. end
  16. 1 def clear
  17. @keys.each { |key| clear_key key }
  18. @keys = ::Set.new
  19. end
  20. 1 def clear_key key
  21. 24 if (subcard = fetch_subcard key)
  22. 24 Director.deep_delete subcard.director
  23. 24 subcard.current_action&.delete
  24. end
  25. 24 Card.cache.soft.delete key
  26. 24 subcard
  27. end
  28. 1 def deep_clear cleared=::Set.new
  29. each_card do |card|
  30. next if cleared.include? card.id
  31. cleared << card.id
  32. card.subcards.deep_clear cleared
  33. end
  34. clear
  35. end
  36. end
  37. end
  38. end

card/lib/card/version.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 module Version
  4. 1 class << self
  5. 1 def release
  6. 224 @@version ||= File.read(File.expand_path("../../VERSION", __dir__)).strip
  7. end
  8. end
  9. end
  10. end

card/lib/card/view.rb

84.09% lines covered

44 relevant lines. 37 lines covered and 7 lines missed.
    
  1. 1 class Card
  2. # Card::View manages {Options view options}, {Cache view caching}, and
  3. # {Permission view permissions}.
  4. #
  5. # View objects, which are instantiated whenever a view is rendered, are available as
  6. # in views and other format methods. The view objects can be accessed using `#voo`.
  7. # We sometimes feebly pretend VOO is an acronym for "view option object," but really
  8. # we just needed a way not to confuse these Card::View options with the countless
  9. # references to viewnames that naturally arise when rendering views within views within
  10. # views.
  11. #
  12. # When view A renders view B within the same format object, A's voo is the parent of
  13. # B's voo. When card C nests card D, a new (sub)format object is initialized. C is then
  14. # the parent _format_ of D, but D has its own root voo.
  15. #
  16. # So a lineage might look something like this:
  17. #
  18. # `F1V1 -> F1V2 -> F1V3 -> F2V1 -> F2V2 -> F3V1 ...`
  19. #
  20. #
  21. 1 class View
  22. 1 include Options
  23. 1 include View::Cache
  24. 1 include Classy
  25. 1 include Permission
  26. 1 extend View::Cache::ClassMethods
  27. 1 attr_reader :format, :parent, :card
  28. 1 class << self
  29. # @return [Symbol] viewname as Symbol
  30. 1 def normalize view
  31. 29550 view.present? ? view.to_sym : nil
  32. end
  33. # @return [Array] list of viewnames as Symbols
  34. 1 def normalize_list val
  35. 51784 case val
  36. 51593 when NilClass then []
  37. 191 when Array then val
  38. when String then val.split(/[\s,]+/)
  39. when Symbol then [val]
  40. else raise Card::Error, "bad show/hide argument: #{val}"
  41. end
  42. end
  43. end
  44. # @param format [Card::Format]
  45. # @param view [Symbol] viewname. Note: Card::View is initialized without a view
  46. # when `voo` is called outside of a render,
  47. # eg `subformat(cardname).method_with_voo_reference`.
  48. # @param raw_options [Hash]
  49. # @param parent [Card::View] (optional)
  50. 1 def initialize format, view, raw_options={}, parent=nil
  51. 23354 @format = format
  52. 23354 @raw_view = view
  53. 23354 @raw_options = raw_options
  54. 23354 @parent = parent
  55. 23354 @card = @format.card
  56. 23354 normalize_options
  57. end
  58. # handle rendering, including optional visibility, permissions, and caching
  59. # @return [rendered view or a stub]
  60. 1 def process
  61. 23354 return if process_live_options == :hide
  62. 43696 fetch { yield ok_view }
  63. end
  64. # the view to "attempt". Typically the same as @raw_view, but @raw_view can
  65. # be overridden, eg for the main view (top view of the main card on a page)
  66. # @return [Symbol] view name
  67. 1 def requested_view
  68. 157817 @requested_view ||= View.normalize live_options[:view]
  69. end
  70. # the final view. can be different from @requested_view when there are
  71. # issues with permissions, recursions, unknown cards, etc.
  72. # @return [Symbol] view name
  73. 1 def ok_view
  74. 65544 @ok_view ||= format.monitor_depth { altered_view || requested_view }
  75. end
  76. # @return [Card::View]
  77. 1 def root
  78. @root = parent ? parent.root : self
  79. end
  80. # @return [true/false]
  81. 1 def root?
  82. !parent
  83. end
  84. # the root voo of the root format
  85. 1 def deep_root
  86. format.root.voo
  87. end
  88. # neither view nor format has a parent
  89. # @return [true/false]
  90. 1 def deep_root?
  91. !parent && !format.parent
  92. end
  93. # next voo object found tracing ancestry through parent voos and/or parent formats
  94. # @return [Card::View]
  95. 1 def next_ancestor across_format=true
  96. 74 parent || (across_format && next_format_ancestor) || nil
  97. end
  98. # voo object of format's parent
  99. 1 def next_format_ancestor
  100. 19597 format.parent&.voo
  101. end
  102. end
  103. end

card/lib/card/view/cache.rb

48.21% lines covered

56 relevant lines. 27 lines covered and 29 lines missed.
    
  1. 1 class Card
  2. 1 class View
  3. # View::Cache supports smart card view caching.
  4. #
  5. # The basic idea is that when view caching is turned on (via `config.view_cache`),
  6. # we try to cache a view whenever it's "safe" to do so. We will include everything
  7. # inside that view (including other views) until we find something that isn't safe.
  8. # When something isn't safe, we render a {Stub stub}: a placeholder
  9. # with all the info we need to come back and replace it with the correct content
  10. # later. In this way it is possible to have many levels of cached views within
  11. # cached views.
  12. #
  13. # Here are some things that we never consider safe to cache:
  14. #
  15. # 1. a view explicitly configured _never_ to be cached
  16. # 2. a view of a card with view-relevant permission restrictions
  17. # 3. a view other than the requested view (eg a denial view)
  18. # 4. a card with unsaved content changes
  19. #
  20. # We also consider it unsafe to cache a view of one card within a view of a different
  21. # card, so nests are always handled with a stub.
  22. #
  23. # ## Cache configuration
  24. #
  25. # Cache settings (#5) can be configured in the
  26. # {Set::Format::AbstractFormat#view view definition}
  27. # and (less commonly) as a {Card::View::Options view option}.
  28. #
  29. # By far, the most common explicit caching configuration is `:never`. This setting
  30. # is used to prevent over-caching, which becomes problematic when data changes
  31. # do not clear the cache.
  32. #
  33. # Generally speaking, a card is smart about clearing its own view caches when
  34. # anything about the card itself. So when I update the card "Johnny", all the cached
  35. # views of "Johnny" are cleared. Similarly, changes to structure rules and other
  36. # basic patterns are typically well managed by the caching system.
  37. #
  38. # However, there are many other potential changes that views cannot detect. Views that
  39. # are susceptible to these "cache hazards" should be configured with `cache: :never`.
  40. #
  41. # ## Cache hazards
  42. #
  43. # If a view contains any of the following cache hazards, it would be wise to consider
  44. # a `cache: :never` configuration:
  45. #
  46. # - dynamic searches (eg `Card.search`) whose results may change
  47. # - live timestamps (eg `Time.now`)
  48. # - environmental variables (eg `Env.params`)
  49. # - any variables altered in one view and used in another (eg `@myvar`)
  50. # - other cards' properties (eg `Card["random"].content`)
  51. #
  52. # What all of the above have in common is that they involve changes about which the
  53. # view caching system is unaware. This means that whether the cache hazard is
  54. # rendered directly in a view or just used in its logic, it can change in a way
  55. # that _should_ change the view but _won't_ change the view if it's cached.
  56. #
  57. # ## Altering cached views
  58. #
  59. # Whereas ignoring cache hazards may cause over-caching, altering cached views
  60. # may cause outright errors. If a view directly alters a rendered view,
  61. # it may be dangerous to cache.
  62. #
  63. # # obviously safe to cache
  64. # view(:x) { "ABC" }
  65. #
  66. # # also safe, because x is NOT altered
  67. # view(:y) { render_x + "DEF" }
  68. #
  69. # # unsafe and thus never cached, because x is altered
  70. # view(:z, cache: :never) { render_x.reverse }
  71. #
  72. # Specifically, the danger is that the inner view will be rendered as a stub,
  73. # and the out view will end up altering the stub and not the view.
  74. #
  75. # Although alterations should be considered dangerous, they are actually only
  76. # problematic in situations where the inner view might sometimes render a stub.
  77. # If the outer view is rendering a view of the _same card_ with all the _same view
  78. # settings_ (perms, unknown, etc), there will be no stub and thus no error.
  79. # Remember, however, that a view on a narrow set may inherit view settings
  80. # from a general set. To be confident that a view alteration is safe, all inherited
  81. # settings must be taken into account.
  82. #
  83. # ## Caching Best Practices
  84. #
  85. # Here are some good rules of thumb to make good use of view caching:
  86. #
  87. # 1. *Use nests.* If you can show the content of a different card with a nest rather
  88. # than by showing the content directly, the caching system will be much
  89. # happier with you.
  90. #
  91. # view :bad_idea, cache: :never do
  92. # Card["random"].content
  93. # end
  94. #
  95. # view :good_idea do
  96. # nest :random, view: :core
  97. # end
  98. #
  99. # 2. *Isolate the cache hazards.* Consider the following variants:
  100. #
  101. # view :bad_idea, cache: :never do
  102. # if morning_for_user?
  103. # expensive_good_morning
  104. # else
  105. # expensive_good_afternoon
  106. # end
  107. # end
  108. #
  109. # view :good_idea, cache: :never do
  110. # morning_for_user? ? render_good_morning : render_good_afternoon
  111. # end
  112. #
  113. # In the first example, we have to generate expensive greetings every time we
  114. # render the view. In the second, only the test is not cached.
  115. #
  116. # 3. If you must alter view results, consider *generating the view content
  117. # in a separate method.*
  118. #
  119. # # First Attempt
  120. #
  121. # view :hash_it_in do
  122. # { cool: false }
  123. # end
  124. #
  125. # view :bad_idea, cache: :never do
  126. # render_badhash.merge sucks: true
  127. # end
  128. #
  129. #
  130. # #Second Attempt
  131. #
  132. # view :hash_it_out do
  133. # hash_it_out
  134. # end
  135. #
  136. # def hash_it_out
  137. # { cool: true }
  138. # end
  139. #
  140. # view :good_idea do
  141. # hash_it_out.merge rocks: true
  142. # end
  143. #
  144. # The first attempt will work fine with caching off but is risky with caching on.
  145. # The second is safe with caching on.
  146. #
  147. # ## Optimizing with `:always`
  148. #
  149. # It is never strictly necessary to use `cache: :always`, but this setting can help
  150. # optimize your use of the caching system in some cases.
  151. #
  152. # Consider the following views:
  153. #
  154. # view(:hat) { "hat" } # ...but imagine this is computationally expensive
  155. #
  156. # view(:old_hat) { "old #{render_hat}" }
  157. # view(:new_hat) { "new #{render_hat}" }
  158. # view(:red_hat) { "red #{render_hat}" }
  159. # view(:blue_hat) { "blue #{render_hat}" }
  160. #
  161. # Whether "hat" uses `:standard` or `:always`, the hat varieties (old, new, etc...)
  162. # will fully contain the rendered hat view in their cache. However, with `:standard`,
  163. # the other views will each re-render hat without attempting to cache it separately
  164. # or to find it in the cache. This could lead to man expensive renderings of the
  165. # "hat" view. By contrast, if the cache setting is `:always`, then hat will be
  166. # cached and retrieved even when it's rendered inside another cached view.
  167. #
  168. 1 module Cache
  169. 1 require "card/view/cache/cache_action"
  170. 1 require "card/view/cache/stub"
  171. 1 include CacheAction
  172. 1 include Stub
  173. 1 private
  174. # render or retrieve view (or stub) with current options
  175. # @param block [Block] code block to render
  176. # @return [rendered view or stub]
  177. 1 def fetch &block
  178. 21848 case cache_action
  179. 21848 when :yield then yield # simple render
  180. when :cache_yield then cache_render(&block) # render to/from cache
  181. when :stub then stub # render stub
  182. end
  183. end
  184. # Fetch view via cache and, when appropriate, render its stubs
  185. #
  186. # If this is a free cache action (see CacheAction), we go through the stubs and
  187. # render them now.
  188. # If the cache is active (ie, we are inside another view), we do not worry about
  189. # stubs but keep going, because the free cache we're inside will take care of
  190. # those stubs.
  191. #
  192. # @return [String (usually)] rendered view
  193. 1 def cache_render
  194. cached_view = cache_fetch { yield }
  195. cache_active? ? cached_view : format.stub_render(cached_view)
  196. end
  197. # Is there already a view cache in progress on which this one depends?
  198. #
  199. # Note that if you create a brand new independent format object
  200. # (ie, not a subformat)
  201. # its activity will be treated as unrelated to this caching/rendering.
  202. #
  203. # @return [true/false]
  204. 1 def cache_active?
  205. deep_root? ? false : self.class.caching?
  206. end
  207. # If view is cached, retrieve it. Otherwise render and store it.
  208. # Uses the primary cache API.
  209. 1 def cache_fetch
  210. caching do
  211. ensure_cache_key
  212. self.class.cache.fetch cache_key do
  213. yield
  214. end
  215. end
  216. end
  217. # keep track of nested cache fetching
  218. 1 def caching
  219. self.class.caching(self) { yield }
  220. end
  221. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  222. # VIEW CACHE KEY
  223. 1 def cache_key
  224. @cache_key ||= [
  225. card_cache_key, format.class, format.nest_mode, options_for_cache_key
  226. ].map(&:to_s).join "-"
  227. end
  228. 1 def card_cache_key
  229. card.real? ? card.id : "#{card.key}-#{card.type_id}"
  230. end
  231. # Registers the cached view for later clearing in the event of related card changes
  232. 1 def ensure_cache_key
  233. card.ensure_view_cache_key cache_key
  234. end
  235. 1 def options_for_cache_key
  236. hash_for_cache_key(live_options) + hash_for_cache_key(viz_hash)
  237. end
  238. 1 def hash_for_cache_key hash
  239. hash.keys.sort.map do |key|
  240. option_for_cache_key key, hash[key]
  241. end.join ";"
  242. end
  243. 1 def array_for_cache_key array
  244. # TODO: needs better handling of edit_structure
  245. # currently we pass complete structure as nested array
  246. array.map do |item|
  247. item.is_a?(Array) ? item.join(":") : item.to_s
  248. end.sort.join ","
  249. end
  250. 1 def option_for_cache_key key, value
  251. "#{key}:#{option_value_to_string value}"
  252. end
  253. 1 def option_value_to_string value
  254. case value
  255. when Hash then "{#{hash_for_cache_key value}}"
  256. when Array then array_for_cache_key(value)
  257. else value.to_s
  258. end
  259. end
  260. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  261. # cache-related Card::View class methods
  262. 1 module ClassMethods
  263. 1 def cache
  264. Card::Cache[Card::View]
  265. end
  266. 1 def caching?
  267. !@caching.nil?
  268. end
  269. 1 def caching voo
  270. old_caching = @caching
  271. @caching = voo
  272. yield
  273. ensure
  274. @caching = old_caching
  275. end
  276. end
  277. end
  278. end
  279. end

card/lib/card/view/cache/cache_action.rb

61.36% lines covered

44 relevant lines. 27 lines covered and 17 lines missed.
    
  1. 1 class Card
  2. 1 class View
  3. 1 module Cache
  4. # determine action to be used in #fetch
  5. 1 module CacheAction
  6. 1 private
  7. # course of action based on config/status/options
  8. # @return [Symbol] :yield, :cache_yield, or
  9. 1 def cache_action
  10. 21848 log_cache_action do
  11. 21848 send "#{cache_status}_cache_action"
  12. end
  13. end
  14. 1 def log_cache_action
  15. 21848 action = yield
  16. # TODO: make configurable
  17. # ...or better yet, integrate into performance logger...
  18. # Rails.logger.warn "VIEW CACHE #{cache_active? ? '-->' : ''}[#{action}] "\
  19. # "(#{card.name}##{requested_view})"
  20. 21848 action
  21. end
  22. # @return [Symbol] :off, :active, or :free
  23. 1 def cache_status
  24. case
  25. 21848 when !cache_on?
  26. 21848 :off # view caching is turned off, format- or system-wide
  27. when cache_active?
  28. :active # another view cache is in progress (current view is inside it)
  29. else
  30. :free # no other cache in progress
  31. end
  32. end
  33. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  34. # CACHE STATUS: OFF
  35. # view caching is turned off, format- or system-wide
  36. # @return [True/False]
  37. 1 def cache_on?
  38. 21848 Card.config.view_cache && format.class.view_caching?
  39. end
  40. # always skip all the magic
  41. 1 def off_cache_action
  42. 21848 :yield
  43. end
  44. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  45. # CACHE STATUS: FREE
  46. # caching is on; no other cache in progress
  47. # @return [Symbol]
  48. 1 def free_cache_action
  49. free_cache_ok? ? :cache_yield : :yield
  50. end
  51. # @return [True/False]
  52. 1 def free_cache_ok?
  53. cache_setting != :never && clean_enough_to_cache?
  54. end
  55. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  56. # CACHE STATUS: ACTIVE
  57. # another view cache is in progress; this view is inside it
  58. # @return [Symbol]
  59. 1 def active_cache_action
  60. active_cache_ok? ? active_cache_action_from_setting : :stub
  61. end
  62. # @return [True/False]
  63. 1 def active_cache_ok?
  64. return false unless parent && clean_enough_to_cache?
  65. return true if normalized_options[:skip_perms]
  66. active_cache_permissible?
  67. end
  68. # apply any permission checks required by view.
  69. # (do not cache views with nuanced permissions)
  70. 1 def active_cache_permissible?
  71. case view_perms
  72. when :none then true
  73. when parent.view_perms then true
  74. when Symbol then format.anyone_can?(view_perms)
  75. else false
  76. end
  77. end
  78. # determine the cache action from the cache setting
  79. # (assuming cache status is "active")
  80. # @return [Symbol] cache action
  81. 1 def active_cache_action_from_setting
  82. level = ACTIVE_CACHE_LEVEL[cache_setting]
  83. level || raise("unknown cache setting: #{cache_setting}")
  84. end
  85. ACTIVE_CACHE_LEVEL =
  86. 1 { always: :cache_yield, standard: :yield, never: :stub }.freeze
  87. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  88. # SHARED METHODS
  89. # @return [Symbol] :standard, :always, or :never
  90. 1 def cache_setting
  91. format.view_cache_setting requested_view
  92. end
  93. # altered view requests and altered cards are not cacheable
  94. # @return [True/False]
  95. 1 def clean_enough_to_cache?
  96. # requested_view == ok_view && !card.unknown? && !card.db_content_changed?
  97. requested_view == ok_view && card.view_cache_clean?
  98. end
  99. end
  100. end
  101. end
  102. end

card/lib/card/view/cache/stub.rb

60.0% lines covered

15 relevant lines. 9 lines covered and 6 lines missed.
    
  1. 1 class Card
  2. 1 class View
  3. 1 module Cache
  4. # A "stub" is a placeholder for a card view.
  5. #
  6. # Cached views use stubs so that _nesting_ content can remained cached
  7. # even while _nested_ content changes. The nested content's place is held
  8. # by a stub.
  9. #
  10. # A stub must contain all the information necessary to produce the view as intended.
  11. 1 module Stub
  12. 1 private
  13. # @return [String]
  14. 1 def stub
  15. "(StUb#{stub_hash.to_json}sTuB)".html_safe
  16. end
  17. 1 def bin_to_hex string
  18. string.unpack("H*").first
  19. end
  20. # @return [Hash]
  21. 1 def stub_hash
  22. { cast: stub_cast,
  23. view_opts: normalized_options.merge(normalized_visibility_options),
  24. format_opts: { nest_mode: format.nest_mode,
  25. override: root?,
  26. context_names: format.context_names } }
  27. # nest mode handling:
  28. #
  29. # Typically modes override views on nests, but stubs create non-standard nests.
  30. # Mode-based view overrides should NOT apply to standard render calls that have
  31. # been replaced with stubs - only to standard nest calls. The override value
  32. # is used to retain this distinction.
  33. end
  34. 1 def stub_cast
  35. cast = card.cast
  36. cast.delete :content if cast[:content].nil?
  37. cast
  38. end
  39. end
  40. end
  41. end
  42. end

card/lib/card/view/classy.rb

76.81% lines covered

69 relevant lines. 53 lines covered and 16 lines missed.
    
  1. 1 class Card
  2. 1 class View
  3. # API to change css classes in other places
  4. 1 module Classy
  5. # Add additional css classes to a css class
  6. #
  7. # Example
  8. # class_up "card-slot", "card-dark text-muted"
  9. #
  10. # If a view later adds the css "card-slot" to a html tag with
  11. #
  12. # classy("card-slot")
  13. #
  14. # then all additional css classes will be added.
  15. #
  16. # The scope when these additional classes apply can be restricted
  17. # @param klass [String, Symbol] the css class to be enriched with additional classes
  18. # @param classier [String, Array<String>] additional css classes
  19. # @param scope [Symbol]
  20. # :view only in the same view
  21. # :subviews the same and all subviews; not in nests or where its nested
  22. # :format all views, sub and parent views; not in nests or where its nested
  23. # :nests the same as :format but also in nests
  24. # :single_use the same as :nests but is removed after the first use
  25. # :global always everywhere
  26. 1 def class_up klass, classier, scope=:subviews
  27. 1349 klass = klass.to_s
  28. 1349 storage_voo(scope).add_extra_classes klass, classier, scope
  29. end
  30. 1 def class_down klass, classier
  31. 74 remove_extra_classes klass, classier, :private
  32. end
  33. 1 def with_class_up klass, classier, scope=:subviews
  34. 74 class_up klass, classier, scope
  35. 74 yield
  36. ensure
  37. 74 class_down klass, classier
  38. end
  39. # don't use in the given block the additional class that
  40. # was added to `klass`
  41. 1 def without_upped_class klass
  42. tmp_class = class_list.delete klass
  43. result = yield tmp_class
  44. class_list[klass] = tmp_class
  45. result
  46. end
  47. 1 def classy *classes
  48. 7814 classes = Array.wrap(classes).flatten
  49. 7814 [classes, extra_classes(classes)].flatten.compact.join " "
  50. end
  51. 1 def add_extra_classes key, classier, scope
  52. 1349 type = class_list_type scope
  53. 1349 class_list(type)[key] =
  54. [class_list(type)[key], classier].flatten.compact.join(" ")
  55. end
  56. # remove classes everywhere where they are visible for the given scope
  57. 1 def remove_extra_classes klass, classier, type
  58. # TODO: scope handling
  59. # Method is not used and maybe no longer necessary with the scope feature
  60. # for class_up.
  61. # It's no longer sufficient to remove only public classes for ancestors.
  62. # Needs an approach similar to extra_classes with the "space" argument
  63. 74 next_ancestor&.remove_extra_classes klass, classier, :public
  64. 74 cl = class_list type
  65. 74 return unless cl[klass]
  66. if cl[klass] == classier
  67. cl.delete klass
  68. else
  69. cl[klass].gsub!(/#{classier}\s?/, "")
  70. end
  71. end
  72. 1 def extra_classes klass
  73. 7814 klass = klass.first if klass.is_a?(Array)
  74. 7814 klass = klass.to_s
  75. 7814 deep_extra_classes klass, :self
  76. end
  77. # recurse through voos and formats to find all extra classes
  78. # @param space [:self, :self_format, :ancestor_format]
  79. 1 def deep_extra_classes klass, space
  80. 36738 [self_extra_classes(klass, space),
  81. ancestor_extra_classes(klass, space)].flatten.compact
  82. end
  83. 1 private
  84. 1 def ancestor_extra_classes klass, space
  85. 36738 if parent
  86. 17215 parent_space = space == :self ? :self_format : :ancestor_format
  87. 17215 parent.deep_extra_classes(klass, parent_space)
  88. else
  89. 19523 next_format_ancestor&.deep_extra_classes(klass, :ancestor_format)
  90. end
  91. end
  92. 1 def storage_voo scope
  93. # When we climb up the voo tree and cross a nest boundary then we can jump only
  94. # to the root voo of the parent format. Hence we have to add classes to the root
  95. # if we want them to be found by nests.
  96. 1349 case scope
  97. 1349 when :view, :subviews then self
  98. when :format, :nests, :single_use then root
  99. when :global then deep_root
  100. else
  101. raise ArgumentError, "invalid class_up scope: #{scope}"
  102. end
  103. end
  104. 1 def self_extra_classes klass, space
  105. 95087 classes = ok_types(space).map { |ot| class_list(ot)[klass] }
  106. 36738 return classes unless class_list(:single_use)&.key? klass
  107. [classes, class_list(:single_use).delete(klass)]
  108. end
  109. 1 def ok_types space
  110. 36738 case space
  111. 22941 when :ancestor_format then [:public]
  112. 5983 when :self_format then %i[public format_private]
  113. 7814 when :self then %i[public format_private private]
  114. end
  115. end
  116. 1 def class_list type=:private
  117. 97859 case type
  118. when :private, :format_private, :public, :single_use
  119. 97859 @class_list ||= {}
  120. 97859 @class_list[type] ||= {}
  121. else
  122. raise ArgumentError, "#{type} not a valid class list"
  123. end
  124. end
  125. # Translates scopes to the privacy types used to manage the class lists.
  126. # A #classy calls looks in the following class_lists:
  127. # private - only in the same voo
  128. # format_private - the same voo and all parent voos in the same format
  129. # public - in all voos in all parent formats
  130. 1 def class_list_type scope
  131. 1349 case scope
  132. when :view
  133. :private
  134. when :format, :subviews
  135. 1349 :format_private
  136. when :nests, :global
  137. :public
  138. when :single_use
  139. :single_use
  140. else
  141. raise ArgumentError, "invalid class_up scope: #{scope}"
  142. end
  143. end
  144. end
  145. end
  146. end

card/lib/card/view/options.rb

66.67% lines covered

15 relevant lines. 10 lines covered and 5 lines missed.
    
  1. 1 class Card
  2. 1 class View
  3. # Manages options for rendering card views
  4. #
  5. # Many options are available to sharks via nests. (See https://decko.org/Nest_Syntax)
  6. #
  7. # {{cardname|hide:menu}}
  8. #
  9. # These options and others are available to monkeys when rendering views
  10. # via #render or #nest.
  11. #
  12. # nest "cardname", hide: :menu
  13. # render :viewname, hide: :menu
  14. #
  15. 1 module Options
  16. # the keymap represents a 2x2 matrix, where the factors are
  17. # (a) whether an option's value can be set by a shark via nests, and
  18. # (b) whether subviews can inherit the option from a parent view.
  19. #
  20. # for sharks | not for sharks
  21. # ________________________________
  22. # inherit | both | heir
  23. # don't inherit | shark | none
  24. #
  25. # (note: each option will likely some day merit its own object)
  26. @keymap = {
  27. 1 shark: [
  28. :view, # view to render
  29. :nest_name, # name as used in nest
  30. :nest_syntax, # full nest syntax
  31. :wrap, # wrap the nest with a wrapper
  32. :show, # render these views when optional
  33. :hide # do not render these views when optional
  34. ], # show/hide can be view (Symbol), list of views (Array),
  35. # or comma separated views (String)
  36. # NOTE: although show and hide are in this non-inheriting group, they are
  37. # actually inherited, just not through the standard mechanism. Because, well,
  38. # they're weird. (See process_visibility)
  39. heir: [
  40. :main, # format object is page's "main" object (Boolean)
  41. :home_view, # view for slot to return to when no view specified
  42. :edit_structure, # use a different structure for editing (Array)
  43. :cql, # contextual cql alterations for search cards (Hash)
  44. :action_id, # a Card::Action id (Integer)
  45. :content_opts # options for Card::Content.new
  46. # :context_names # names used to contextualize titles
  47. ],
  48. both: [
  49. :help, # cue text when editing
  50. :structure, # overrides the content of the card
  51. :title, # overrides the name of the card
  52. :variant, # override the canonical version of the name with a different
  53. # variant
  54. :input_type, # inline_nests makes a form within standard content (Symbol)
  55. :type, # set the default type of new cards
  56. :size, # set an image size
  57. # (also used for character limit in one_line_content)
  58. :params, # parameters for add button. deprecated!
  59. :items, # options for items (Hash)
  60. :cache, # change view cache behaviour
  61. # (Symbol<:always, :standard, :never>)
  62. :edit, # edit mode
  63. # (Symbol<:inline, :standard, :full>)
  64. :separator, # item separator in certain lists
  65. :filter
  66. ],
  67. none: [
  68. :skip_perms, # do not check permissions for this view (Boolean)
  69. :main_view, # this is main view of page (Boolean)
  70. :layout #
  71. ]
  72. }
  73. # Note: option values are strings unless otherwise noted
  74. 1 class << self
  75. 1 attr_reader :keymap
  76. 1 def add_option name, type
  77. raise "invalid option type: #{type}" unless @keymap.key?(type)
  78. @keymap[type] << name
  79. reset_key_lists
  80. VooApi.define_getter name
  81. VooApi.define_setter name
  82. end
  83. end
  84. 1 extend KeyLists
  85. 1 include VooApi
  86. 1 include Visibility
  87. end
  88. end
  89. end

card/lib/card/view/options/key_lists.rb

83.33% lines covered

18 relevant lines. 15 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 class View
  3. 1 module Options
  4. 1 module KeyLists
  5. # all standard option keys
  6. # @return [Array]
  7. 1 def all_keys
  8. 73417 @all_keys ||= keymap.each_with_object([]) { |(_k, v), a| a.push(*v) }
  9. end
  10. # keys whose values can be set by Sharks in card nests
  11. # @return [Array]
  12. 1 def shark_keys
  13. 3864 @shark_keys ||= ::Set.new(keymap[:both]) + keymap[:shark]
  14. end
  15. # keys that follow simple standard inheritance pattern from parent views
  16. # @return [Array]
  17. 1 def heir_keys
  18. 16732 @heir_keys ||= ::Set.new(keymap[:both]) + keymap[:heir]
  19. end
  20. # Keys that can be read or written via accessors
  21. # @return [Array]
  22. 1 def accessible_keys
  23. 1 all_keys - [ # (all but the following)
  24. :view, # view is accessed as requested_view or ok_view and cannot be
  25. # directly manipulated
  26. :show, :hide # these have a more extensive API (see Card::View::Visibility)
  27. ]
  28. end
  29. 1 def slot_keys
  30. 1516 @slot_keys ||= all_keys - [:skip_perms]
  31. end
  32. 1 def reset_key_lists
  33. @all_keys = nil
  34. @shark_keys = nil
  35. @heir_keys = nil
  36. end
  37. end
  38. end
  39. end
  40. end

card/lib/card/view/options/visibility.rb

93.18% lines covered

44 relevant lines. 41 lines covered and 3 lines missed.
    
  1. 1 class Card
  2. 1 class View
  3. 1 module Options
  4. # manages showing and hiding optional view renders
  5. 1 module Visibility
  6. # tracks show/hide value for each view with an explicit setting
  7. # eg { toggle: :hide }
  8. 1 def viz_hash
  9. 138081 @viz_hash ||= {}
  10. end
  11. # test methods
  12. 1 def hide? view
  13. 24511 viz_hash[view&.to_sym] == :hide
  14. end
  15. 1 def show? view
  16. 1319 !hide? view
  17. end
  18. # write methods
  19. 1 def show *views
  20. 94 viz views, :show
  21. end
  22. 1 def hide *views
  23. 954 viz views, :hide
  24. end
  25. # force write methods
  26. 1 def show! *views
  27. 1 viz views, :show, true
  28. end
  29. 1 def hide! *views
  30. 118 viz views, :hide, true
  31. end
  32. # advanced write method
  33. 1 VIZ_SETTING = { show: :show, true => :show,
  34. hide: :hide, false => :hide, nil => :hide }.freeze
  35. 1 def viz views, setting, force=false
  36. 74934 Array.wrap(views).flatten.each do |view|
  37. 23833 view = view.to_sym
  38. 23833 next if !force && viz_hash[view]
  39. 23569 viz_hash[view] = VIZ_SETTING[setting]
  40. end
  41. end
  42. 1 def visible? view
  43. 172 viz view, yield unless viz_hash[view]
  44. 172 show? view
  45. end
  46. # test whether view is optional
  47. # (@optional is set in normalize_options
  48. # @return [true/false]
  49. 1 def optional?
  50. 23354 @optional
  51. end
  52. # translate raw hide, show options (which can be strings, symbols,
  53. # arrays, etc)
  54. 1 def process_visibility
  55. 25892 viz_hash.reverse_merge! parent.viz_hash if parent
  56. 25892 process_visibility_options live_options
  57. 25892 viz requested_view, @optional if @optional && !viz_hash[requested_view]
  58. end
  59. 1 private
  60. # if true, #process returns nil
  61. 1 def hide_requested_view?
  62. 23354 optional? && hide?(requested_view)
  63. end
  64. # takes an options_hash and processes it to update viz_hash
  65. 1 def process_visibility_options options_hash
  66. 25892 %i[hide show].each do |setting|
  67. 51784 views = View.normalize_list(options_hash.delete(setting)).map(&:to_sym)
  68. 51784 viz views, setting, true
  69. end
  70. end
  71. 1 def normalized_visibility_options
  72. viz_hash.each_with_object({}) do |(key, val), hash|
  73. hash[val] ||= []
  74. hash[val] << key
  75. end
  76. end
  77. end
  78. end
  79. end
  80. end

card/lib/card/view/options/voo_api.rb

92.22% lines covered

90 relevant lines. 83 lines covered and 7 lines missed.
    
  1. 1 class Card
  2. 1 class View
  3. 1 module Options
  4. # VooApi methods let developers use view options dynamically.
  5. 1 module VooApi
  6. # There are two primary options hashes:
  7. # - @normalized_options are determined upon initialization and do not change
  8. # after that.
  9. # @return [Hash] options
  10. 1 attr_reader :normalized_options
  11. 1 class << self
  12. 1 def included base
  13. # Developers can also set most options directly via accessors,
  14. # eg voo.title = "King"
  15. # :view, :show, and :hide have non-standard access (see #accessible_keys)
  16. 1 base.accessible_keys.each do |option_key|
  17. 25 define_getter option_key unless option_key == :items
  18. 25 define_setter option_key
  19. end
  20. end
  21. 1 def define_getter option_key
  22. 24 define_method option_key do
  23. 60171 live_options[option_key]
  24. end
  25. end
  26. 1 def define_setter option_key
  27. 25 define_method "#{option_key}=" do |value|
  28. 728 live_options[option_key] = special_option_value(option_key, value) || value
  29. end
  30. end
  31. end
  32. # "items", the option used to configure views of each of a list of cards, is
  33. # currently the only Hash option (thus this accessor override)
  34. # @return [Hash]
  35. 1 def items
  36. 255 live_options[:items] ||= {}
  37. end
  38. # options to be used in data attributes of card slots (normalized options
  39. # with standard keys)
  40. # FIXME: what we really want is options as they were when render was called.
  41. # normalized is wrong because it can get changed before render. live is wrong
  42. # because they can get changed after. current solution is a compromise.
  43. # @return [Hash]
  44. 1 def slot_options
  45. 1200 normalized_options.merge(view: requested_view).slice(*Options.slot_keys)
  46. end
  47. # ACCESSOR_HELPERS
  48. # methods that follow the normalize_#{key} pattern are called by accessors
  49. # (arguably that should be done during normalization!)
  50. 1 def normalize_special_options! opts
  51. 23354 opts.each do |option_key, value|
  52. 55134 new_value = special_option_value option_key, value
  53. 55134 opts[option_key] = new_value if new_value
  54. end
  55. end
  56. 1 def special_option_value option_key, value
  57. 55862 try "normalize_#{option_key}", value
  58. end
  59. 1 def normalize_input_type value
  60. value&.to_sym
  61. end
  62. 1 def normalize_edit value
  63. 246 value&.to_sym
  64. end
  65. 1 def normalize_cache value
  66. value&.to_sym
  67. end
  68. 1 def normalize_wrap value
  69. 224 value = value.split(",").map(&:strip) if value.is_a? String
  70. 224 Array.wrap(value).compact.flatten
  71. end
  72. 1 protected
  73. # - @live_options are dynamic and can be altered by the "voo" API at any time.
  74. # Such alterations are NOT used in the stub of the current voo, but they
  75. # ARE inherited by children voos.
  76. #
  77. # @return [Hash]
  78. 1 def live_options
  79. 428308 @live_options ||= process_live_options
  80. end
  81. 1 private
  82. # option normalization includes standardizing options into a hash with
  83. # symbols as keys, managing standard view inheritance, and special
  84. # handling for main_views.
  85. 1 def normalize_options
  86. 23354 @normalized_options = opts = options_to_hash @raw_options.clone
  87. 23354 normalize_special_options! opts
  88. 23354 @optional = opts.delete(:optional) || false
  89. 23354 add_implicit_options!
  90. 23354 inherit_options_from_parent!
  91. 23354 validate_options! opts
  92. 23354 opts
  93. end
  94. 1 def add_implicit_options!
  95. 23354 @normalized_options[:view] = @raw_view
  96. 23354 @normalized_options[:main] = true if format.main?
  97. # opts[:context_names] = format.context_names
  98. end
  99. # typically options are already a hash. this also handles an array of
  100. # hashes and nil.
  101. 1 def options_to_hash opts
  102. 23354 case opts
  103. when ActionController::Parameters
  104. opts.to_unsafe_h.deep_symbolize_keys
  105. 23354 when Hash then opts.deep_symbolize_keys!
  106. when Array then opts[0].merge(opts[1]).deep_symbolize_keys!
  107. when nil then {}
  108. else raise Card::Error, "bad view options: #{opts}"
  109. end
  110. end
  111. # standard inheritance from parent view object
  112. 1 def inherit_options_from_parent!
  113. 23354 return unless parent
  114. 16732 Options.heir_keys.each do |option_key|
  115. 317908 inherit_from_parent! option_key
  116. end
  117. end
  118. 1 def inherit_from_parent! option_key
  119. 317908 return unless (parent_value = parent.live_options[option_key])
  120. 23843 @normalized_options[option_key] ||= parent_value
  121. end
  122. 1 def process_live_options
  123. 23354 @live_options = normalized_options.clone
  124. 23354 process_main_nest_options
  125. 23354 process_before_view
  126. 23354 process_visibility
  127. 23354 return :hide if hide_requested_view? # bail to avoid unnecessary processing
  128. 21848 process_view_wrappers
  129. 21848 @live_options
  130. end
  131. # This method triggers the "before" blocks which can alter the @live_options
  132. # hash both directly and indirectly (via the voo API)
  133. 1 def process_before_view
  134. 23354 format.before_view requested_view
  135. end
  136. # adds the wrappers assigned to ok_view in view definition
  137. 1 def process_view_wrappers
  138. 21848 view_wrappers = format.view_setting(:wrap, ok_view)
  139. 21848 return unless view_wrappers.present?
  140. 628 @live_options[:wrap] = Array.wrap(@live_options[:wrap])
  141. 628 if view_wrappers.is_a? ::Hash
  142. 127 view_wrappers.each_pair do |name, opts|
  143. 127 @live_options[:wrap] << [name, opts]
  144. end
  145. else
  146. 501 @live_options[:wrap] += Array.wrap(view_wrappers)
  147. end
  148. end
  149. # merge the options of the main nest into the @live_options
  150. # They are not processed in normalize_options so that
  151. # they're NOT locked in the stub.
  152. 1 def process_main_nest_options
  153. 23354 @live_options.merge! format.main_nest_options if @live_options[:main_view]
  154. end
  155. 1 def validate_options! opts
  156. 23354 return unless (foreign_opts = foreign_options_in opts)
  157. # TODO: this should raise a UserError if the options come directly from params
  158. # (eg, mycard?slot[badoption]=true should not be treated as a server error)
  159. raise Card::Error, "illegal view options: #{foreign_opts}"
  160. end
  161. # find non-standard option in Hash
  162. # @param opts [Hash] options hash
  163. # @return [Hash] options Hash
  164. 1 def foreign_options_in opts
  165. 96765 foreign_opts = opts.reject { |k, _v| Options.all_keys.include? k }
  166. 23354 foreign_opts.empty? ? nil : foreign_opts
  167. end
  168. end
  169. end
  170. end
  171. end

card/lib/card/view/permission.rb

100.0% lines covered

25 relevant lines. 25 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class View
  3. # View permissions support view-specific permission handling
  4. #
  5. # Views can be configured in {Set::Format::AbstractFormat#view view definitions}
  6. # with the `perms` directive, eg
  7. #
  8. # # only render if user has permission to update card
  9. # view :myview, perms: :update do...
  10. 1 module Permission
  11. 1 def view_perms
  12. 32881 @view_perms = setting(:perms) || :read
  13. end
  14. 1 private
  15. 1 def altered_view
  16. 21848 return if skip_check?
  17. 8375 alter_unknown || denial
  18. end
  19. 1 def skip_check?
  20. 21848 normalized_options[:skip_perms] || view_perms == :none
  21. end
  22. 1 def setting setting_name, view=nil
  23. 41256 view ||= requested_view
  24. 41256 format.view_setting setting_name, view
  25. end
  26. # by default views can't handle unknown cards, but this can be overridden in
  27. # view definitions with the `unknown` directive
  28. 1 def alter_unknown
  29. 8375 setting = setting(:unknown)
  30. 8375 return if setting == true || card.known?
  31. 29 setting.is_a?(Symbol) ? setting : format.view_for_unknown(requested_view)
  32. end
  33. 1 def denial
  34. 8346 return unless (task = denied_task)
  35. 527 format.view_for_denial requested_view, task
  36. end
  37. 1 def denied_task
  38. 8346 if view_perms.is_a? Proc
  39. 982 :read unless view_perms.call(format) # read isn't quite right
  40. else
  41. 14728 Array.wrap(view_perms).find { |task| !format.ok? task }
  42. end
  43. end
  44. end
  45. end
  46. end

card/mod/core/chunk/escaped_literal.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Content
  4. 1 module Chunk
  5. # These are basic chunks that have a pattern and can be protected.
  6. # They are used by rendering process to prevent wiki rendering
  7. # occuring within literal areas such as <code> and <pre> blocks
  8. # and within HTML tags.
  9. 1 class EscapedLiteral < Abstract
  10. 1 FULL_RE = { "[" => /\A\\\[\[[^\]]*\]\]/,
  11. "{" => /\A\\\{\{[^\}]*\}\}/ }.freeze
  12. 1 Card::Content::Chunk.register_class self,
  13. prefix_re: '\\\\(?:\\[\\[|\\{\\{)',
  14. idx_char: '\\'
  15. 1 def self.full_re prefix
  16. 8 FULL_RE[prefix[1, 1]]
  17. end
  18. 1 def interpret match, _content
  19. 8 @process_chunk = match[0].sub(/^\\(.)/, format.escape_literal('\1'))
  20. end
  21. end
  22. end
  23. end
  24. end

card/mod/core/chunk/keep_escaped_literal.rb

100.0% lines covered

10 relevant lines. 10 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Content
  4. 1 module Chunk
  5. # These are basic chunks that have a pattern and can be protected.
  6. # This chunk is used for markdown processing to ensure that
  7. # the escaping survives the markdown rendering.
  8. 1 class KeepEscapedLiteral < Abstract
  9. 1 FULL_RE = { "[" => /\A\\\[\[[^\]]*\]\]/,
  10. "{" => /\A\\\{\{[^\}]*\}\}/ }.freeze
  11. 1 Card::Content::Chunk.register_class self,
  12. prefix_re: '\\\\(?:\\[\\[|\\{\\{)',
  13. idx_char: '\\'
  14. 1 def self.full_re prefix
  15. 8 FULL_RE[prefix[1, 1]]
  16. end
  17. 1 def interpret match, _content
  18. 8 @process_chunk = match[0].sub(/^\\(.)/, '\\\\\\\\\1')
  19. end
  20. end
  21. end
  22. end
  23. end

card/mod/core/chunk/link.rb

75.81% lines covered

62 relevant lines. 47 lines covered and 15 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # require File.expand_path("../reference", __FILE__)
  3. 1 load File.expand_path("../reference.rb", __FILE__)
  4. 1 class Card
  5. 1 class Content
  6. 1 module Chunk
  7. # extend ActiveSupport::Autoload
  8. # autoload :Reference , "reference"
  9. 1 class Link < Card::Content::Chunk::Reference
  10. 1 CODE = "L".freeze # L for "Link"
  11. 1 attr_reader :link_text
  12. # Groups: $1, [$2]: [[$1]] or [[$1|$2]] or $3, $4: [$3][$4]
  13. 1 Card::Content::Chunk.register_class self,
  14. prefix_re: '\\[\\[',
  15. full_re: /\A\[\[([^\]]+)\]\]/,
  16. idx_char: "["
  17. 1 def reference_code
  18. 10 CODE
  19. end
  20. 1 def interpret match, _content
  21. target, @link_text =
  22. 2080 if (raw_syntax = match[1])
  23. 2080 if (i = divider_index(raw_syntax)) # [[A | B]]
  24. 1718 [raw_syntax[0..(i - 1)], raw_syntax[(i + 1)..-1]]
  25. else # [[ A ]]
  26. 362 [raw_syntax, nil]
  27. end
  28. end
  29. 2080 @link_text = objectify @link_text
  30. 2080 if target.match? %r{^(/|https?:|mailto:)}
  31. 724 @explicit_link = objectify target
  32. else
  33. 1356 @name = target
  34. end
  35. end
  36. 1 def divider_index string
  37. # there's probably a better way to do the following.
  38. # point is to find the first pipe that's not inside an nest
  39. 2080 return unless string.index "|"
  40. 1718 string_copy = string.dup
  41. 1718 string.scan(/\{\{[^\}]*\}\}/) do |incl|
  42. string_copy.gsub! incl, ("x" * incl.length)
  43. end
  44. 1718 string_copy.index "|"
  45. end
  46. # view options
  47. 1 def options
  48. 1233 link_text ? { title: link_text } : {}
  49. end
  50. 1 def objectify raw
  51. 2804 return unless raw
  52. 2442 raw.strip!
  53. 2442 if raw =~ /(^|[^\\])\{\{/
  54. Card::Content.new raw, format
  55. else
  56. 2442 raw
  57. end
  58. end
  59. 1 def render_link view: :link, explicit_link_opts: {}
  60. 1942 @link_text = render_obj @link_text
  61. 1942 if @explicit_link
  62. 709 @explicit_link = render_obj @explicit_link
  63. 709 format.link_to_resource @explicit_link, @link_text, explicit_link_opts
  64. 1233 elsif @name
  65. 1233 format.with_nest_mode :normal do
  66. 1233 format.nest referee_name, options.merge(view: view)
  67. end
  68. end
  69. end
  70. 1 def link_target
  71. if @explicit_link
  72. render_obj @explicit_link
  73. elsif @name
  74. referee_name
  75. end
  76. end
  77. 1 def process_chunk
  78. 150 @process_chunk ||= render_link
  79. end
  80. 1 def inspect
  81. "<##{self.class}:e[#{@explicit_link}]n[#{@name}]l[#{@link_text}]" \
  82. "p[#{@process_chunk}] txt:#{@text}>"
  83. end
  84. 1 def replace_reference old_name, new_name
  85. replace_name_reference old_name, new_name
  86. replace_link_text old_name, new_name
  87. @text =
  88. @link_text.nil? ? "[[#{referee_name}]]" : "[[#{referee_name}|#{@link_text}]]"
  89. end
  90. 1 def replace_link_text old_name, new_name
  91. if @link_text.is_a?(Card::Content)
  92. @link_text.find_chunks(Card::Content::Chunk::Reference).each do |chunk|
  93. chunk.replace_reference old_name, new_name
  94. end
  95. elsif @link_text.present?
  96. @link_text = old_name.to_name.sub_in(@link_text, with: new_name)
  97. end
  98. end
  99. 1 def explicit_link?
  100. 1792 @explicit_link
  101. end
  102. end
  103. end
  104. end
  105. end

card/mod/core/chunk/nest.rb

81.82% lines covered

66 relevant lines. 54 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # require File.expand_path("../reference", __FILE__)
  3. 1 class Card
  4. 1 class Content
  5. 1 module Chunk
  6. # Handler for nest chunks: {{example}}
  7. 1 class Nest < Reference
  8. 1 attr_reader :options
  9. 1 DEFAULT_OPTION = :view # a value without a key is interpreted as view
  10. 1 Chunk.register_class(self, prefix_re: '\\{\\{',
  11. full_re: /\A\{\{([^\{\}]*)\}\}/,
  12. idx_char: "{")
  13. 1 def interpret match, _content
  14. 3881 in_brackets = strip_tags match[1]
  15. 3881 name, @opt_lists = in_brackets.split "|", 2
  16. 3881 name = name.to_s.strip
  17. 3881 if name =~ /^\#/
  18. 15 @process_chunk = name =~ /^\#\#/ ? "" : visible_comment(in_brackets)
  19. else
  20. 3866 @options = interpret_options.merge nest_name: name,
  21. nest_syntax: in_brackets
  22. 3866 @name = name
  23. end
  24. end
  25. 1 def strip_tags string
  26. # note: not using ActionView's strip_tags here
  27. # because this needs to be super fast.
  28. 3881 string.gsub(/\<[^\>]*\>/, "")
  29. end
  30. 1 def visible_comment message
  31. "<!-- #{CGI.escapeHTML message} -->"
  32. end
  33. 1 def interpret_options
  34. 3866 raw_options = @opt_lists.to_s.split("|").reverse
  35. 3866 raw_options.inject(nil) do |prev_level, level_options|
  36. 3640 interpret_piped_options level_options, prev_level
  37. end || {}
  38. end
  39. 1 def interpret_piped_options list_string, items
  40. 3640 options_hash = items.nil? ? {} : { items: items }
  41. 3640 option_string_to_hash list_string, options_hash
  42. 3640 options_hash
  43. end
  44. 1 def option_string_to_hash list_string, options_hash
  45. 3640 each_option(list_string) do |key, value|
  46. 3864 key = key.to_sym
  47. 3864 if key == :item
  48. options_hash[:items] ||= {}
  49. options_hash[:items][:view] = value
  50. 3864 elsif Card::View::Options.shark_keys.include? key
  51. 3864 options_hash[key] = value
  52. # else
  53. # handle other keys
  54. end
  55. end
  56. end
  57. 1 def inspect
  58. "<##{self.class}:n[#{@name}] p[#{@process_chunk}] txt:#{@text}>"
  59. end
  60. 1 def process_chunk
  61. 3661 return @process_chunk if @process_chunk
  62. 3646 referee_name
  63. 3646 @processed = format.content_nest(@options)
  64. # this is not necessarily text, sometimes objects for json
  65. end
  66. 1 def replace_reference old_name, new_name
  67. replace_name_reference old_name, new_name
  68. nest_body = [@name.to_s, @opt_lists].compact * "|"
  69. @text = "{{#{nest_body}}}"
  70. end
  71. 1 def explicit_view= view
  72. return if @options[:view]
  73. # could check to make sure it's not already the default...
  74. if @text =~ /\|/
  75. @text.sub! "|", "|#{view};"
  76. else
  77. @text.sub! "}}", "|#{view}}}"
  78. end
  79. end
  80. 1 def main?
  81. 4 nest_name == "_main"
  82. end
  83. 1 def nest_name
  84. 4 options&.dig :nest_name
  85. end
  86. 1 def self.gsub string
  87. 50 string.gsub(/\{\{([^\}]*)\}\}/) do |_match|
  88. 143 yield(Regexp.last_match[1])
  89. end
  90. end
  91. 1 def raw_options
  92. @opt_lists
  93. end
  94. 1 private
  95. 1 def each_option attr_string
  96. 3640 return if attr_string.blank?
  97. 3640 attr_string.strip.split(";").each do |pair|
  98. # key is optional for view option
  99. 3864 value, key = pair.split(":", 2).reverse
  100. 3864 key ||= self.class::DEFAULT_OPTION.to_s
  101. 3864 yield key.strip, value.strip
  102. end
  103. end
  104. end
  105. end
  106. end
  107. end

card/mod/core/chunk/query_reference.rb

85.19% lines covered

27 relevant lines. 23 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Content
  4. 1 module Chunk
  5. # This should find +Alfred+ in expressions like
  6. # 1) {"name":"Alfred"}
  7. # 2a) {"name":["in","Alfred"]}
  8. # 3a) {"plus_right":["Alfred"]}
  9. # but not in
  10. # 2b) "content":"foo", "Alfred":"bar"
  11. # 3b) {"name":["Alfred", "Toni"]} ("Alfred" is an operator here)
  12. # It's not possible to distinguish between 2a) and 2b) or 3a) and 3b) with a
  13. # simple regex, hence we use a too general regex and check for query keywords
  14. # after the match, which of course means that we don't find references with
  15. # query keywords as name
  16. 1 require File.expand_path("reference", __dir__)
  17. 1 class QueryReference < Reference
  18. 1 QUERY_KEYWORDS = ::Set.new(
  19. (
  20. 1 ::Card::Query::MODIFIERS.keys +
  21. ::Card::Query::OPERATORS.keys +
  22. ::Card::Query::ATTRIBUTES.keys +
  23. ::Card::Query::CONJUNCTIONS.keys +
  24. %w[desc asc count]
  25. ).map(&:to_s)
  26. )
  27. 1 Card::Content::Chunk.register_class(
  28. self, prefix_re: '(?<=[:,\\[])\\s*"',
  29. # we check for colon, comma or square bracket before a quote
  30. # we have to use a lookbehind, otherwise
  31. # if the colon matches it would be
  32. # identified mistakenly as an URI chunk
  33. full_re: /\A\s*"([^"]+)"/,
  34. idx_char: '"'
  35. )
  36. # OPTIMIZE: instead of comma or square bracket check for operator followed
  37. # by comma or "plus_right"|"plus_left"|"plus" followed by square bracket
  38. # something like
  39. # prefix_patterns = [
  40. # "\"\\s*(?:#{Card::Query::OPERATORS.keys.join('|')})\"\\s*,",
  41. # "\"\\s*(?:#{Card::Query::PLUS_ATTRIBUTES}.keys
  42. # .join('|')})\\s*:\\s*\\[\\s*",
  43. # "\"\\s*(?:#{(QUERY_KEYWORDS - Card::Query::PLUS_ATTRIBUTES)
  44. # .join('|')})\"\\s*:",
  45. # ]
  46. # prefix_re: '(?<=#{prefix_patterns.join('|')})\\s*"'
  47. # But: What do we do with the "in" operator? After the first value there is
  48. # no prefix which we can use to detect the following values as
  49. # QueryReference chunks
  50. 1 class << self
  51. 1 def full_match content, prefix
  52. # matches cardnames that are not keywords
  53. # FIXME: would not match cardnames that are keywords
  54. 64 match, offset = super(content, prefix)
  55. 64 return if !match || keyword?(match[1])
  56. 64 [match, offset]
  57. end
  58. 1 def keyword? str
  59. 64 return unless str
  60. 64 QUERY_KEYWORDS.include?(str.tr(" ", "_").downcase)
  61. end
  62. end
  63. 1 def interpret match, _content
  64. 64 @name = match[1]
  65. end
  66. 1 def process_chunk
  67. @process_chunk ||= @text
  68. end
  69. 1 def inspect
  70. "<##{self.class}:n[#{@name}] p[#{@process_chunk}] txt:#{@text}>"
  71. end
  72. 1 def replace_reference old_name, new_name
  73. replace_name_reference old_name, new_name
  74. @text = "\"#{@name}\""
  75. end
  76. 1 def reference_code
  77. 64 "Q" # for "Query"
  78. end
  79. end
  80. end
  81. end
  82. end

card/mod/core/chunk/reference.rb

73.33% lines covered

30 relevant lines. 22 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Content
  4. 1 module Chunk
  5. 1 class Reference < Abstract
  6. 1 attr_accessor :referee_name, :name
  7. 1 def referee_name
  8. 5394 return if name.nil?
  9. 5394 @referee_name ||= referee_name_from_rendered(render_obj(name))
  10. 5394 @referee_name = @referee_name.absolute(card.name).to_name
  11. rescue Card::Error::NotFound
  12. # do not break on missing id/codename references.
  13. end
  14. 1 def referee_name_from_rendered rendered_name
  15. 5160 ref_card = fetch_referee_card rendered_name
  16. 5160 ref_card ? ref_card.name : rendered_name.to_name
  17. end
  18. 1 def referee_card
  19. 51 @referee_card ||= referee_name && Card.fetch(referee_name)
  20. end
  21. 1 def replace_name_reference old_name, new_name
  22. @referee_card = nil
  23. @referee_name = nil
  24. if name.is_a? Card::Content
  25. name.find_chunks(Chunk::Reference).each do |chunk|
  26. chunk.replace_reference old_name, new_name
  27. end
  28. else
  29. @name = name.to_name.swap old_name, new_name
  30. end
  31. end
  32. 1 def render_obj raw
  33. 7811 if format && raw.is_a?(Card::Content)
  34. format.process_content raw
  35. else
  36. 7811 raw
  37. end
  38. end
  39. 1 private
  40. 1 def fetch_referee_card rendered_name
  41. 5160 case rendered_name # FIXME: this should be standard fetch option.
  42. when /^\~(\d+)$/ # get by id
  43. Card.fetch Regexp.last_match(1).to_i
  44. when /^\:(\w+)$/ # get by codename
  45. 234 Card.fetch Regexp.last_match(1).to_sym
  46. end
  47. end
  48. end
  49. end
  50. end
  51. end

card/mod/core/chunk/uri.rb

86.79% lines covered

53 relevant lines. 46 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 require "uri"
  3. # This wiki chunk matches arbitrary URIs, using patterns from the Ruby URI
  4. # modules.
  5. # It parses out a variety of fields that could be used by formats to format
  6. # the links in various ways (shortening domain names, hiding email addresses)
  7. # It matches email addresses and host.com.au domains without schemes (http://)
  8. # but adds these on as required.
  9. #
  10. # The heuristic used to match a URI is designed to err on the side of caution.
  11. # That is, it is more likely to not autolink a URI than it is to accidently
  12. # autolink something that is not a URI. The reason behind this is it is easier
  13. # to force a URI link by prefixing 'http://' to it than it is to escape and
  14. # incorrectly marked up non-URI.
  15. #
  16. # I'm using a part of the [ISO 3166-1 Standard][iso3166] for country name
  17. # suffixes.
  18. # The generic names are from www.bnoack.com/data/countrycode2.html)
  19. # [iso3166]: http://geotags.com/iso3166/
  20. 1 module Card::Content::Chunk
  21. 1 class URI < Abstract
  22. 1 SCHEMES = %w[irc http https ftp ssh git sftp file ldap ldaps mailto].freeze
  23. 6 REJECTED_PREFIX_RE = %w{! ": " ' ](}.map { |s| Regexp.escape s } * "|"
  24. 1 attr_reader :uri, :link_text
  25. 1 delegate :to, :scheme, :host, :port, :path, :query, :fragment, to: :uri
  26. 1 Card::Content::Chunk.register_class(
  27. self, prefix_re: "(?:(?!#{REJECTED_PREFIX_RE})(?:#{SCHEMES * '|'})\\:)",
  28. full_re: /\A#{::URI.regexp(SCHEMES)}/,
  29. idx_char: ":"
  30. )
  31. 1 class << self
  32. 1 def full_match content, prefix
  33. 6 prepend_str = if prefix[-1, 1] != ":" && config[:prepend_str]
  34. 6 config[:prepend_str]
  35. else
  36. ""
  37. end
  38. 6 content = prepend_str + content
  39. 6 match = super content, prefix
  40. 6 [match, prepend_str.length]
  41. end
  42. 1 def context_ok? content, chunk_start
  43. 6 preceding_string = content[chunk_start - 2..chunk_start - 1]
  44. 6 preceding_string !~ /(?:#{REJECTED_PREFIX_RE})$/
  45. end
  46. end
  47. 1 def interpret match, _content
  48. 6 chunk = match[0]
  49. 6 last_char = chunk[-1, 1]
  50. 6 chunk.gsub!(/(?:&nbsp;)+/, "")
  51. @trailing_punctuation =
  52. 6 if %w[, . ) ! ? :].member?(last_char)
  53. @text.chop!
  54. chunk.chop!
  55. last_char
  56. end
  57. 6 chunk.sub!(/\.$/, "")
  58. 6 @link_text = chunk
  59. 6 @uri = ::URI.parse(chunk)
  60. 6 @process_chunk = process_uri_chunk
  61. rescue ::URI::Error => e
  62. # warn "rescue parse #{chunk_class}:
  63. # '#{m}' #{e.inspect} #{e.backtrace*"\n"}"
  64. Rails.logger.warn "rescue parse #{self.class}: #{e.inspect}"
  65. end
  66. 1 private
  67. 1 def process_text
  68. @link_text
  69. end
  70. 1 def process_uri_chunk
  71. 6 link = format.link_to_resource @link_text, process_text
  72. 6 "#{link}#{@trailing_punctuation}"
  73. end
  74. end
  75. # FIXME: DRY, merge these two into one class
  76. 1 class EmailURI < URI
  77. 1 PREPEND_STR = "mailto:".freeze
  78. 1 EMAIL = '[a-zA-Z\\d](?:[-a-zA-Z\\d.]*[a-zA-Z\\d])?\\@'.freeze
  79. 1 Card::Content::Chunk.register_class(
  80. self, prefix_re: "(?:(?!#{REJECTED_PREFIX_RE})#{EMAIL})\\b",
  81. full_re: /\A#{::URI.regexp(SCHEMES)}/,
  82. prepend_str: PREPEND_STR,
  83. idx_char: "@"
  84. )
  85. # removes the prepended string from the unchanged match text
  86. 1 def process_text
  87. 6 @text = @text.sub(/^mailto:/, "")
  88. end
  89. end
  90. 1 class HostURI < URI
  91. GENERIC = "aero|biz|com|coop|edu|gov|info|int|mil|" \
  92. 1 "museum|name|net|org".freeze
  93. COUNTRY = "ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|" \
  94. "bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cf|cd|cg|" \
  95. "ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|" \
  96. "ec|ee|eg|eh|er|es|et|fi|fj|fk|fm|fo|fr|fx|ga|gb|gd|ge|gf|gh|" \
  97. "gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|" \
  98. "il|in|io|iq|ir|is|it|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|" \
  99. "kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|" \
  100. "mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|" \
  101. "no|np|nr|nt|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pt|pw|py|" \
  102. "qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|" \
  103. "st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|" \
  104. "tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|" \
  105. "za|zm|zr|zw|" \
  106. 1 "eu".freeze # made this separate, since it's not technically
  107. # a country -efm
  108. # These are needed otherwise HOST will match almost anything
  109. 1 TLDS = "(?:#{GENERIC}|#{COUNTRY})".freeze
  110. # TLDS = "(?:#{GENERIC})"
  111. 1 PREPEND_STR = "http://".freeze
  112. 1 HOST = "(?:[a-zA-Z\\d](?:[-a-zA-Z\\d]*[a-zA-Z\\d])?\\.)+#{TLDS}".freeze
  113. 1 Card::Content::Chunk.register_class(
  114. self, prefix_re: "(?:(?!#{REJECTED_PREFIX_RE})#{HOST})\\b",
  115. full_re: /\A#{::URI.regexp(SCHEMES)}/,
  116. prepend_str: PREPEND_STR
  117. )
  118. # removes the prepended string from the unchanged match text
  119. 1 def process_text
  120. @text = @text.sub(%r{^http://}, "")
  121. end
  122. end
  123. end

card/mod/core/chunk/view_stub.rb

48.39% lines covered

31 relevant lines. 15 lines covered and 16 lines missed.
    
  1. 1 class Card
  2. 1 class Content
  3. 1 module Chunk
  4. 1 class ViewStub < Abstract
  5. 1 Chunk.register_class(
  6. self,
  7. prefix_re: Regexp.escape("(StUb"),
  8. full_re: /\A\(StUb(.*?)sTuB\)/m,
  9. idx_char: "("
  10. )
  11. 1 def initialize text, content
  12. super
  13. end
  14. 1 def interpret match, _content
  15. @stub_hash = initial_stub_hash match[1]
  16. interpret_hash_values
  17. end
  18. 1 def initial_stub_hash string
  19. JSON.parse(string).symbolize_keys
  20. # MessagePack.unpack(hex_to_bin(string)).symbolize_keys
  21. end
  22. 1 def hex_to_bin string
  23. string.scan(/../).map { |x| x.hex.chr }.join
  24. end
  25. 1 def interpret_hash_values
  26. @stub_hash.keys.each do |key|
  27. send "interpret_#{key}"
  28. end
  29. end
  30. 1 def interpret_cast
  31. @stub_hash[:cast].symbolize_keys!
  32. end
  33. 1 def interpret_view_opts
  34. @stub_hash[:view_opts].symbolize_keys!
  35. end
  36. 1 def interpret_format_opts
  37. hash = @stub_hash[:format_opts]
  38. hash.symbolize_keys!
  39. hash[:nest_mode] = hash[:nest_mode].to_sym
  40. hash[:override] = hash[:override] == "true"
  41. hash[:context_names].map!(&:to_name)
  42. end
  43. 1 def process_chunk
  44. @processed = format.stub_nest @stub_hash
  45. end
  46. 1 def result
  47. @processed
  48. end
  49. end
  50. end
  51. end
  52. end

card/mod/core/format/data_format.rb

100.0% lines covered

3 relevant lines. 3 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Format
  3. 1 class DataFormat < Format
  4. end
  5. end
  6. end

card/mod/core/format/html_format.rb

100.0% lines covered

20 relevant lines. 20 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # # require "card/content/diff"
  3. 1 class Card
  4. 1 class Format
  5. # Main Format class for formatting card views in HTML
  6. 1 class HtmlFormat < Format
  7. 1 register :html
  8. 1 attr_accessor :options_need_save, :start_time, :skip_autosave
  9. 1 def main?
  10. 24156 !@main.nil?
  11. end
  12. # is the current card the requested card?
  13. 1 def focal?
  14. 595 @focal ||= show_layout? ? main? : depth.zero?
  15. end
  16. 1 def default_nest_view
  17. # FIXME: not sure this makes sense as a rule...
  18. 143 card.rule(:default_html_view) || :titled
  19. end
  20. 1 def default_item_view
  21. 56 :bar
  22. end
  23. 1 def escape_literal literal
  24. 8 "<span>#{literal}</span>"
  25. end
  26. 1 def mime_type
  27. 274 "text/html"
  28. end
  29. 1 def final_render_call method
  30. 21498 rendered = super
  31. 21498 rendered.is_a?(Array) ? output(rendered) : rendered
  32. end
  33. end
  34. end
  35. end

card/mod/core/format/text_format.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. 1 class Format
  4. 1 class TextFormat < Format
  5. 1 register :text
  6. 1 register :txt
  7. 1 aliases["txt"] = "text"
  8. 1 def self.view_caching?
  9. # probably overkill. problem was with email text message
  10. false
  11. end
  12. end
  13. end
  14. end

card/mod/core/lib/card/fetch.rb

96.77% lines covered

31 relevant lines. 30 lines covered and 1 lines missed.
    
  1. 1 class Card
  2. # retrieve card from cache or database, or (where needed) instantiate new card
  3. 1 class Fetch
  4. 1 include Retrieve
  5. 1 include Results
  6. 1 include Store
  7. 1 attr_reader :card, :mark, :opts
  8. # see arg options in all/fetch
  9. 1 def initialize *args
  10. 219438 normalize_args args
  11. 219438 absolutize_mark
  12. 219438 validate_opts!
  13. end
  14. 1 def retrieve_or_new
  15. 144896 retrieve_existing
  16. 144896 update_cache
  17. 144896 results
  18. end
  19. 1 def local_only?
  20. 9220 opts[:local_only]
  21. end
  22. 1 def skip_modules?
  23. 213243 opts[:skip_modules]
  24. end
  25. 1 def normalize_args args
  26. 219438 @opts = args.last.is_a?(Hash) ? args.pop : {}
  27. 219438 @mark = Card.id_or_name args
  28. end
  29. 1 def absolutize_mark
  30. 219438 return unless mark.name? && (supercard = opts.dig(:new, :supercard))
  31. 6320 @mark = mark.absolute_name supercard.name
  32. end
  33. 1 def validate_opts!
  34. 219438 return unless opts[:new] && opts[:skip_virtual]
  35. raise Card::Error, "fetch called with new args and skip_virtual"
  36. end
  37. 1 def look_in_trash?
  38. 15927 @opts[:look_in_trash]
  39. end
  40. 1 def skip_type_lookup?
  41. 6502 opts[:skip_virtual] || opts[:skip_type_lookup] # || opts[:new]
  42. end
  43. end
  44. end

card/mod/core/lib/card/fetch/results.rb

97.3% lines covered

74 relevant lines. 72 lines covered and 2 lines missed.
    
  1. 1 class Card
  2. 1 class Fetch
  3. # polishing fetch results
  4. 1 module Results
  5. 1 def results
  6. 144896 return if card.nil?
  7. 111127 card.new_card? ? new_result_card : finalize_result_card
  8. end
  9. 1 def finalize_result_card
  10. 101342 card.include_set_modules unless skip_modules?
  11. 101342 card
  12. end
  13. 1 def new_result_card
  14. 13433 with_new_card do
  15. 3648 finalize_result_card
  16. # must include_set_modules before checking `card.known?`,
  17. # in case, eg, set modules override #virtual?
  18. 3648 card if new_opts || card.known?
  19. end
  20. end
  21. 1 def with_new_card
  22. 13433 if new_opts
  23. 2586 quick_renew || renew
  24. 10847 elsif opts[:skip_virtual]
  25. 9785 return nil
  26. else
  27. 1062 assign_name_from_mark
  28. end
  29. 3648 yield
  30. end
  31. 1 def renew
  32. 102 return if new_opts.blank?
  33. # Rails.logger.info "renewing: #{mark}, #{new_opts}"
  34. 102 @card = card.dup
  35. 102 @card.newish newish_opts
  36. end
  37. 1 def newish_opts
  38. 102 hash = new_opts.clone.reverse_merge name: mark
  39. 102 if (content = assignable_content(hash.delete(:default_content)))
  40. hash[:content] = content
  41. end
  42. 102 hash[:type_lookup] = :force if @force_type_lookup
  43. 102 hash
  44. end
  45. 1 def quick_renew
  46. 2586 return false unless quick_renew?
  47. # Rails.logger.info "QUICK renewing: #{mark}, #{new_opts}"
  48. 2484 update_supercard
  49. 2484 assign_name_from_mark
  50. 2484 true
  51. end
  52. 1 def update_supercard
  53. 2484 return unless (sc = new_opts[:supercard])
  54. 595 @card.supercard = sc
  55. 595 @card.update_superleft @card.name
  56. end
  57. 1 def quick_renew?
  58. 2586 return true if new_opts.blank?
  59. 1036 return false if type_change? || name_change?
  60. 934 return false if fancy_renew?
  61. 934 quick_content
  62. 934 true
  63. end
  64. # contains subcards, etc, that quick_renew can't handle
  65. 1 def fancy_renew?
  66. 934 test_opts = new_opts.slice :supercard, :name, :type_id, :type, :type_code,
  67. :content, :default_content
  68. 934 new_opts.keys.size > test_opts.keys.size
  69. end
  70. 1 def assignable_content default_content
  71. 1036 new_opts[:content] || (@card.db_content.blank? && default_content)
  72. end
  73. 1 def quick_content
  74. 934 return unless (content = assignable_content(new_opts[:default_content]))
  75. @card.content = content
  76. end
  77. 1 def name_change?
  78. 944 return false unless (new_name = new_opts[:name]&.to_name)
  79. 255 return false if new_name.relative? && mark.name? && mark.absolute?
  80. 199 new_name.to_s != @card.name.to_s
  81. end
  82. 1 def type_change?
  83. 1036 return true if @card.type_id.nil?
  84. 1036 type_id = type_id_from_new_opts
  85. 1036 return true if !type_id && supercard_might_change_type?
  86. 995 type_id && (type_id != @card.type_id)
  87. end
  88. 1 def type_id_from_new_opts
  89. 1036 type_id = new_opts[:type_id] || new_opts[:type] || new_opts[:type_code]&.to_sym
  90. 1036 type_id.is_a?(Symbol) ? Codename.id(type_id) : type_id
  91. end
  92. 1 def supercard_might_change_type?
  93. # ...via type_plus_right rule
  94. 299 sc = new_opts[:supercard]
  95. 299 @force_type_lookup = sc&.new? && (sc.type_id != sc.default_type_id)
  96. end
  97. 1 def new_opts
  98. 29238 @new_opts ||= opts[:new]
  99. end
  100. 1 def assign_name_from_mark
  101. 3546 return if opts[:local_only]
  102. 2450 return unless mark&.to_s != card.name
  103. 21 card.name = mark.to_s
  104. end
  105. end
  106. end
  107. end

card/mod/core/lib/card/fetch/retrieve.rb

100.0% lines covered

30 relevant lines. 30 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Fetch
  3. # retrieval and instantiation methods for Card::Fetch
  4. 1 module Retrieve
  5. # look for card in cache. if that doesn't work, look in database
  6. 1 def retrieve_existing
  7. 144896 return unless mark.present?
  8. 111116 retrieve_from_cache || retrieve_from_db
  9. end
  10. 1 def retrieve_from_cache
  11. 111116 @card = Card.send "retrieve_from_cache_by_#{mark_type}",
  12. mark_value, @opts[:local_only]
  13. 111116 @card = nil if card&.new? && look_in_trash?
  14. # don't return cached cards if looking in trash -
  15. # we want the db version
  16. 111116 card
  17. end
  18. 1 def retrieve_from_db
  19. 8347 query = retrieval_from_db_query
  20. 8347 @card = query ? Card.where(query).take : nil
  21. 8347 @cache_ready = true if card.present? && !card.trash
  22. 8347 card
  23. end
  24. 1 def retrieval_from_db_query
  25. 8347 return unless (query = retrieval_from_db_query_base)
  26. 5454 query[:trash] = false unless look_in_trash?
  27. 5454 query
  28. end
  29. 1 def retrieval_from_db_query_base
  30. 8347 if mark_type == :key && mark.simple?
  31. 1659 { key: mark_value }
  32. 6688 elsif (id = id_from_mark)
  33. 3795 { id: id }
  34. end
  35. end
  36. 1 def id_from_mark
  37. 6688 mark_type == :id ? mark_value : Lexicon.id(mark_value)
  38. end
  39. # In both the cache and the db, ids and keys are used to retrieve card data.
  40. # These methods identify the kind of mark to use and its value
  41. 1 def mark_type
  42. 126151 @mark_type ||= mark.is_a?(Integer) ? :id : :key
  43. end
  44. 1 def mark_value
  45. 119463 @mark_value ||= mark.is_a?(Integer) ? mark : mark.key
  46. end
  47. end
  48. end
  49. end

card/mod/core/lib/card/fetch/store.rb

100.0% lines covered

31 relevant lines. 31 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 class Fetch
  3. # lazy cache updates based on results
  4. 1 module Store
  5. 1 def update_cache
  6. 144896 return unless update_cache?
  7. 9220 prep_for_cache
  8. 9220 Card.write_to_cache card, local_only?
  9. end
  10. 1 def update_cache?
  11. 144896 (cache_ready? || new_for_cache || needs_prep?) && !card&.uncacheable?
  12. end
  13. 1 def cache_ready?
  14. 144896 @cache_ready
  15. end
  16. # instantiate a card as a cache placeholder
  17. 1 def new_for_cache
  18. 139540 return unless new_for_cache?
  19. 3090 args = { name: mark, skip_modules: true }
  20. 3090 args[:type_lookup] = :skip if skip_type_lookup?
  21. 3090 args.merge! new_opts.slice(:type, :type_id, :type_code) if eager_caching?
  22. 3090 @card = Card.new args
  23. end
  24. 1 def eager_caching?
  25. 3090 opts[:eager_cache] && mark.name? && mark.absolute? && new_opts.present?
  26. end
  27. 1 def new_for_cache?
  28. 139540 reusable? && new_card_needed?
  29. end
  30. 1 def needs_prep?
  31. 136450 return unless card.present?
  32. 102681 !(skip_modules? || card.patterns?)
  33. end
  34. 1 def new_card_needed?
  35. 78466 !(card.present? && (card.type_id.present? || skip_type_lookup?))
  36. end
  37. 1 def reusable?
  38. 139540 !(mark.is_a?(Integer) || (mark.blank? && !opts[:new]))
  39. end
  40. # Because Card works by including set-specific ruby modules on singleton classes and
  41. # singleton classes generally can't be cached, we can never cache the cards in a
  42. # completely ready-to-roll form.
  43. #
  44. # However, we can optimize considerably by saving the list of ruby modules in
  45. # environments where they won't be changing (eg production) or at least the list of
  46. # matching set patterns
  47. 1 def prep_for_cache
  48. # return # DELETE ME
  49. 9220 return if skip_modules?
  50. 3975 Cardio.config.cache_set_module_list ? card.set_modules : card.patterns
  51. end
  52. end
  53. end
  54. end

card/mod/core/lib/card/rule.rb

93.1% lines covered

29 relevant lines. 27 lines covered and 2 lines missed.
    
  1. 1 class Card
  2. # Optimized handling of card "rules" (Set+Setting) and preferences.
  3. 1 module Rule
  4. 1 class << self
  5. 1 def global_setting name
  6. 1675 Auth.as_bot do
  7. 1675 (card = Card[name]) && !card.db_content.strip.empty? && card.db_content
  8. end
  9. end
  10. 1 def toggle val
  11. 3 val.to_s.strip == "1"
  12. end
  13. 1 def rule_cache
  14. 18582 Cache.read
  15. end
  16. 1 def preference_cache
  17. 365 PreferenceCache.read
  18. end
  19. 1 def read_rule_cache
  20. 573 ReadRuleCache.read
  21. end
  22. 1 def clear_rule_cache
  23. 39 Cache.clear
  24. end
  25. 1 def clear_preference_cache
  26. 43 PreferenceCache.clear
  27. end
  28. 1 def clear_read_rule_cache
  29. ReadRuleCache.clear
  30. end
  31. 1 def preference_names user_name, setting_code
  32. 4 Card.search({ right: { codename: setting_code },
  33. left: { left: { type_id: SetID },
  34. right: user_name },
  35. return: :name },
  36. "preference cards for user: #{user_name}")
  37. end
  38. 1 def all_user_ids_with_rule_for set_card, setting_code
  39. 954 cache_key = "#{set_card.rule_cache_key_base}+#{setting_code}"
  40. 954 user_ids = PreferenceCache.user_ids[cache_key] || []
  41. 954 user_ids.include?(AllID) ? all_user_ids : user_ids
  42. end
  43. 1 private
  44. 1 def all_user_ids
  45. Card.where(type_id: UserID).pluck :id
  46. end
  47. end
  48. end
  49. end

card/mod/core/lib/card/rule/cache.rb

100.0% lines covered

29 relevant lines. 29 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Rule
  3. 1 class Cache
  4. 1 class_attribute :sql, :cache_key
  5. 1 self.sql = %(
  6. SELECT
  7. rules.id AS rule_id,
  8. settings.id AS setting_id,
  9. sets.id AS set_id,
  10. sets.left_id AS anchor_id,
  11. sets.right_id AS set_tag_id
  12. FROM cards rules
  13. JOIN cards sets ON rules.left_id = sets.id
  14. JOIN cards settings ON rules.right_id = settings.id
  15. WHERE sets.type_id = #{SetID}
  16. AND settings.type_id = #{SettingID}
  17. AND (settings.codename != 'follow' OR rules.db_content != '')
  18. AND rules.trash is false
  19. AND sets.trash is false
  20. AND settings.trash is false;
  21. ).freeze
  22. 1 self.cache_key = "RULES".freeze
  23. 1 class << self
  24. 1 def read
  25. 19520 Card.cache.read(cache_key) || populate
  26. end
  27. 1 def populate
  28. 149 Card.cache.write cache_key, lookup_hash
  29. end
  30. 1 def clear
  31. 82 Card.cache.write cache_key, nil
  32. end
  33. 1 def lookup_hash
  34. 131 rows.each_with_object({}) do |row, hash|
  35. 18817 next unless (key = lookup_key row)
  36. 18817 hash[key] = row["rule_id"].to_i
  37. end
  38. end
  39. 1 def lookup_key row
  40. 19660 return false unless (setting_code = setting_code(row))
  41. 19660 anchor_id = row["anchor_id"]
  42. 19660 return false unless (pattern_code = pattern_code(anchor_id, row))
  43. 19660 [anchor_id, pattern_code, setting_code].compact.map(&:to_s).join "+"
  44. end
  45. 1 def pattern_code anchor_id, row
  46. 19660 set_class_id = anchor_id.nil? ? row["set_id"] : row["set_tag_id"]
  47. 19660 Card::Codename[set_class_id.to_i]
  48. end
  49. 1 def setting_code row
  50. 19660 Card::Codename[row["setting_id"].to_i]
  51. end
  52. 1 def rows
  53. 149 Card.connection.select_all sql
  54. end
  55. end
  56. end
  57. end
  58. end

card/mod/core/lib/card/rule/preference_cache.rb

100.0% lines covered

30 relevant lines. 30 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Rule
  3. 1 class PreferenceCache < Cache
  4. 1 self.sql = %(
  5. SELECT
  6. preferences.id AS rule_id,
  7. settings.id AS setting_id,
  8. sets.id AS set_id,
  9. sets.left_id AS anchor_id,
  10. sets.right_id AS set_tag_id,
  11. users.id AS user_id
  12. FROM cards preferences
  13. JOIN cards user_sets ON preferences.left_id = user_sets.id
  14. JOIN cards settings ON preferences.right_id = settings.id
  15. JOIN cards users ON user_sets.right_id = users.id
  16. JOIN cards sets ON user_sets.left_id = sets.id
  17. WHERE sets.type_id = #{SetID}
  18. AND settings.type_id = #{SettingID}
  19. AND (users.type_id = #{UserID} or users.codename = 'all')
  20. AND sets.trash is false
  21. AND settings.trash is false
  22. AND users.trash is false
  23. AND user_sets.trash is false
  24. AND preferences.trash is false;
  25. ).freeze
  26. 1 self.cache_key = "PREFERENCES".freeze
  27. 1 USER_ID_CACHE_KEY = "USER_IDS".freeze
  28. 1 class << self
  29. 1 def user_ids
  30. 1008 Card.cache.read(USER_ID_CACHE_KEY) || (populate && user_ids)
  31. end
  32. 1 def populate
  33. 54 @rows = nil
  34. 54 super.tap do
  35. 54 populate_user_ids
  36. 54 @rows = nil
  37. end
  38. end
  39. 1 def populate_user_ids
  40. 54 Card.cache.write USER_ID_CACHE_KEY, user_id_hash
  41. end
  42. 1 def user_id_hash
  43. 54 rows.each_with_object({}) do |row, hash|
  44. 843 key = lookup_key_without_user row
  45. 843 hash[key] ||= []
  46. 843 hash[key] << row["user_id"]
  47. end
  48. end
  49. 1 def clear
  50. 43 super
  51. 43 Card.cache.write USER_ID_CACHE_KEY, nil
  52. end
  53. 1 def rows
  54. 108 @rows ||= super
  55. end
  56. 1 alias :lookup_key_without_user :lookup_key
  57. 1 def lookup_key row
  58. 843 return unless (base = lookup_key_without_user row)
  59. 843 "#{base}+#{row['user_id']}"
  60. end
  61. end
  62. end
  63. end
  64. end

card/mod/core/lib/card/rule/read_rule_cache.rb

100.0% lines covered

11 relevant lines. 11 lines covered and 0 lines missed.
    
  1. 1 class Card
  2. 1 module Rule
  3. 1 class ReadRuleCache < Cache
  4. 1 self.sql = %(
  5. SELECT
  6. refs.referee_id AS party_id,
  7. read_rules.id AS read_rule_id
  8. FROM cards read_rules
  9. JOIN card_references refs ON refs.referer_id = read_rules.id
  10. JOIN cards sets ON read_rules.left_id = sets.id
  11. WHERE read_rules.right_id = #{ReadID}
  12. AND sets.type_id = #{SetID}
  13. AND read_rules.trash is false
  14. AND sets.trash is false;
  15. ).freeze
  16. 1 self.cache_key = "READRULES".freeze
  17. 1 class << self
  18. 1 def lookup_hash
  19. 18 rows.each_with_object({}) do |row, h|
  20. 504 party_id = row["party_id"].to_i
  21. 504 h[party_id] ||= []
  22. 504 h[party_id] << row["read_rule_id"].to_i
  23. end
  24. end
  25. end
  26. end
  27. end
  28. end

card/mod/settings/lib/card/setting.rb

85.42% lines covered

48 relevant lines. 41 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 class Card
  3. # Used to extend setting modules like Card::Set::Self::Create in the
  4. # settings mod
  5. 1 module Setting
  6. # Let M = Card::Setting (module)
  7. # E = Card::Set::Self::Create (module extended with M)
  8. # O = Card['*create'] (object)
  9. # accessible in E
  10. 1 attr_accessor :codename
  11. # accessible in E and M
  12. 1 mattr_accessor :groups, :group_names, :user_specific
  13. 1 def self.extended host_class
  14. # accessible in E and O
  15. 26 host_class.mattr_accessor :restricted_to_type, :rule_type_editable, :short_help_text,
  16. :raw_help_text, :right_set, :applies
  17. 26 setting_class_name = host_class.to_s.split("::").last
  18. 63 host_class.ensure_set { "Card::Set::Right::#{setting_class_name}" }
  19. 26 host_class.right_set = Card::Set::Right.const_get(setting_class_name)
  20. 26 host_class.right_set.mattr_accessor :raw_help_text
  21. end
  22. 1 def self.codenames
  23. Card::Setting.groups.values.flatten.compact.map(&:codename)
  24. end
  25. @@group_names = {
  26. 1 templating: "Templating",
  27. permission: "Permissions",
  28. webpage: "Webpage",
  29. editing: "Editing",
  30. event: "Events",
  31. other: "Other",
  32. config: "Config"
  33. }
  34. 1 @@groups = @@group_names.keys.each_with_object({}) do |key, groups|
  35. 7 groups[key] = []
  36. end
  37. 1 @@user_specific = ::Set.new
  38. 1 def self.user_specific? codename
  39. @@user_specific.include? codename
  40. end
  41. # usage:
  42. # setting_opts group: :permission | :event | ...
  43. # position: <Fixnum> (starting at 1, default: add to end)
  44. # rule_type_editable: true | false (default: false)
  45. # restricted_to_type: <cardtype> | [ <cardtype>, ...]
  46. 1 def setting_opts opts
  47. 26 group = opts[:group] || :other
  48. 26 @@groups[group] ||= []
  49. 26 set_position group, opts[:position]
  50. 26 @codename = opts[:codename] ||
  51. name.match(/::(\w+)$/)[1].underscore.to_sym
  52. 26 self.rule_type_editable = opts[:rule_type_editable]
  53. 26 self.restricted_to_type = permitted_type_ids opts[:restricted_to_type]
  54. 26 self.short_help_text = opts[:short_help_text]
  55. 26 self.applies = opts[:applies]
  56. 26 right_set.raw_help_text = self.raw_help_text = opts[:help_text]
  57. 26 return unless opts[:user_specific]
  58. 1 @@user_specific << @codename
  59. end
  60. 1 def set_position group, pos
  61. 26 if pos
  62. 26 if @@groups[group][pos - 1]
  63. 2 @@groups[group].insert(pos - 1, self)
  64. else
  65. 24 @@groups[group][pos - 1] = self
  66. end
  67. else
  68. @@groups[group] << self
  69. end
  70. end
  71. 1 def applies_to_cardtype type_id, prototype=nil
  72. (!restricted_to_type || restricted_to_type.include?(type_id)) &&
  73. (!prototype || applies_to_prototype?(prototype))
  74. end
  75. 1 def applies_to_prototype? prototype
  76. return true unless applies
  77. applies.call(prototype)
  78. end
  79. 1 private
  80. 1 def permitted_type_ids types
  81. 26 return unless types
  82. 2 type_ids = Array.wrap(types).flatten.map do |cardtype|
  83. 6 Card::Codename.id cardtype
  84. end
  85. 2 ::Set.new(type_ids)
  86. end
  87. end
  88. end

card/mod/standard/lib/card/layout.rb

76.92% lines covered

52 relevant lines. 40 lines covered and 12 lines missed.
    
  1. 1 class Card
  2. 1 class Layout
  3. 1 class << self
  4. 1 def render layout, format
  5. layout_class(layout).new(layout, format).render
  6. end
  7. 1 def layout_class layout
  8. 2 if layout.respond_to? :call
  9. Card::Layout::ProcLayout
  10. 2 elsif card_layout? layout
  11. 2 Card::Layout::CardLayout
  12. elsif code_layout? layout
  13. Card::Layout::CodeLayout
  14. else
  15. Card::Layout::UnknownLayout
  16. end
  17. end
  18. 1 def card_layout? name
  19. 226 Card.fetch_type_id(name).in? [Card::LayoutTypeID, Card::HtmlID, Card::BasicID]
  20. rescue ArgumentError, Card::Error::CodenameNotFound => _e
  21. false
  22. end
  23. 1 def code_layout? name
  24. built_in_layouts_hash.key? name.to_sym
  25. end
  26. 1 def register_layout new_layout
  27. 6 key = layout_key new_layout
  28. 6 return if layouts[key]
  29. 6 layouts[key] = block_given? ? yield : {}
  30. end
  31. 1 def deregister_layout layout_name
  32. layouts.delete layout_key(layout_name)
  33. end
  34. 1 def layout_key name
  35. 230 return name if name.is_a? Symbol
  36. 226 name.to_name.key.to_sym
  37. end
  38. 1 def register_built_in_layout new_layout, opts
  39. 8 register_layout(new_layout) { opts.present? ? opts : nil }
  40. 4 built_in_layouts_hash[new_layout] = true
  41. end
  42. 1 def built_in_layouts_hash
  43. 4 @built_in_layouts ||= {}
  44. end
  45. 1 def built_in_layouts
  46. built_in_layouts_hash.keys
  47. end
  48. 1 def layouts
  49. 236 @layouts ||= {}
  50. end
  51. 1 def clear_cache
  52. @built_in_layouts = @layouts = nil
  53. end
  54. 1 def main_nest_opts layout_name, format
  55. 224 key = layout_key layout_name
  56. 224 opts = layouts[key] || register_layout_with_nest(layout_name, format)
  57. 224 opts.clone
  58. end
  59. 1 def register_layout_with_nest layout_name, format
  60. 2 register_layout(layout_name) do
  61. 2 layout_class(layout_name).new(layout_name, format).fetch_main_nest_opts
  62. end
  63. end
  64. end
  65. 1 def initialize layout, format
  66. 226 @layout = layout
  67. 226 @format = format
  68. end
  69. 1 def fetch_main_nest_opts
  70. {}
  71. end
  72. 1 def main_nest_opts
  73. self.class.main_nest_opts @layout, @format
  74. end
  75. end
  76. end

card/mod/standard/lib/card/layout/card_layout.rb

78.26% lines covered

23 relevant lines. 18 lines covered and 5 lines missed.
    
  1. 1 class Card
  2. 1 class Layout
  3. # Layout based on a card's content
  4. 1 class CardLayout < Layout
  5. 1 def layout_card
  6. 226 @layout_card ||= Card.quick_fetch @layout
  7. end
  8. 1 def render
  9. 224 @format.process_content layout_card.content, chunk_list: :references
  10. end
  11. 1 def fetch_main_nest_opts
  12. 2 main_nest = find_main_nest_chunk
  13. # don't .& me !! (can be false)
  14. 2 (main_nest && main_nest.options) ||
  15. raise(Card::Error, "no main nest found in layout \"#{@layout}\"")
  16. end
  17. 1 MAIN_NESTING_LIMIT = 5
  18. 1 def find_main_nest_chunk card=layout_card, depth=0
  19. 2 content = Card::Content.new(card.content, @format, chunk_list: :nest_only)
  20. 2 return false unless content.each_chunk.count.positive?
  21. 2 main_chunk(content) || go_deeper(content, depth)
  22. end
  23. 1 def go_deeper content, depth
  24. return false if depth > MAIN_NESTING_LIMIT
  25. content.each_chunk do |chunk|
  26. main_chunk = find_main_nest_chunk chunk.referee_card, depth + 1
  27. return main_chunk if main_chunk
  28. end
  29. false
  30. end
  31. 1 def main_chunk content
  32. 2 content.each_chunk.find(&:main?)
  33. end
  34. end
  35. end
  36. end

card/tmpsets/set/mod001-utility/abstract/bs_badge.rb

56.25% lines covered

16 relevant lines. 9 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (BsBadge)
  4. #
  5. 1 module BsBadge;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/bs_badge.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def labeled_badge count, label, opts={}
  10. haml :labeled_badge, badge_haml_opts(count, label, opts)
  11. end
  12. 1 def tab_badge count, label, opts={}
  13. haml :tab_badge, badge_haml_opts(count, label, opts)
  14. end
  15. 1 def badge_haml_opts count, label, opts
  16. process_badge_opts count, opts
  17. { count: count, label: label, klass: opts[:klass], color: opts[:color],
  18. title: opts[:title] }
  19. end
  20. 1 def process_badge_opts count, opts
  21. if count.try(:zero?) && !opts[:zero_ok]
  22. opts[:klass] = [opts[:klass], "disabled-o"].compact.join " "
  23. end
  24. opts[:color] ||= "secondary"
  25. end
  26. end
  27. end;end;end;end;
  28. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/bs_badge.rb ~~

card/tmpsets/set/mod001-utility/abstract/filterable.rb

53.85% lines covered

13 relevant lines. 7 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Filterable)
  4. #
  5. 1 module Filterable;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/filterable.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def filterable filter_hash={}, html_opts={}
  10. add_class html_opts, "_filterable _noFilterUrlUpdates"
  11. html_opts[:data] ||= {}
  12. html_opts[:data][:filter] = filter_hash
  13. wrap_with :div, yield, html_opts
  14. end
  15. 1 def filtering selector=nil
  16. selector ||= "._filter-widget:visible"
  17. wrap_with :div, yield, class: "_filtering", "data-filter-selector": selector
  18. end
  19. end
  20. end;end;end;end;
  21. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/filterable.rb ~~

card/tmpsets/set/mod001-utility/abstract/filterable_bar.rb

63.64% lines covered

11 relevant lines. 7 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (FilterableBar)
  4. #
  5. 1 module FilterableBar;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/filterable_bar.rb"; end
  8. 1 include_set Abstract::Filterable
  9. 1 before :bar do
  10. class_up "bar-body", "_filterable"
  11. super()
  12. end
  13. 1 before :expanded_bar do
  14. class_up "bar", "_filterable"
  15. super()
  16. end
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/filterable_bar.rb ~~

card/tmpsets/set/mod001-utility/abstract/utility.rb

60.0% lines covered

10 relevant lines. 6 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Utility)
  4. #
  5. 1 module Utility;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/utility.rb"; end
  8. 1 def fetch_params params
  9. Env.params.select { |key, val| val && params.include?(key) }
  10. .with_indifferent_access
  11. end
  12. 1 def param_to_i key, default
  13. if (value = Env.params[key])
  14. value.to_i
  15. else
  16. default
  17. end
  18. end
  19. end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/utility/set/abstract/utility.rb ~~

card/tmpsets/set/mod002-admin/self/admin.rb

28.77% lines covered

73 relevant lines. 21 lines covered and 52 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Admin"
  4. #
  5. # collect arrays of the form
  6. 1 module Admin;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/admin.rb"; end
  9. # [task symbol, { execute_policy: block, stats_policy: block }]
  10. 1 basket :tasks
  11. 1 def run_task_from_task_basket task
  12. task = task.to_sym
  13. task_data = tasks.find {|h| h[:name].to_sym == task.to_sym}
  14. if !irreversibles_tasks_allowed? && task_data[:irreversible]
  15. not_allowed task_data[:stats][:link_text]
  16. else
  17. task_data[:execute_policy].call if task_data
  18. end
  19. end
  20. 1 event :admin_tasks, :initialize, on: :update do
  21. return unless (task = Env.params[:task])
  22. raise Card::Error::PermissionDenied, self unless Auth.always_ok?
  23. case task.to_sym
  24. when :clear_cache then Card::Cache.reset_all
  25. when :repair_references then Card::Reference.repair_all
  26. when :repair_permissions then Card.repair_all_permissions
  27. when :clear_solid_cache then Card.clear_solid_cache
  28. when :clear_machine_cache then Card.reset_all_machines
  29. when :clear_script_cache then Card.reset_script_machine
  30. when :clear_history
  31. not_allowed "clear history" unless irreversibles_tasks_allowed?
  32. Card::Action.delete_old
  33. else
  34. run_task_from_task_basket task
  35. end
  36. abort :success
  37. end
  38. 1 def not_allowed task
  39. raise Card::Error::PermissionDenied,
  40. "The admin task '#{task}' is disabled for security reasons.<br>"\
  41. "You can enable it with the config option 'allow_irreversible_admin_tasks'"
  42. end
  43. 1 def irreversibles_tasks_allowed?
  44. Cardio.config.allow_irreversible_admin_tasks
  45. end
  46. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  47. 1 view :core, cache: :never do
  48. stats = card_stats
  49. stats += cache_stats
  50. stats += memory_stats
  51. card.tasks.each do |task|
  52. stats += Array.wrap task[:stats]
  53. end
  54. table_content = stats.map { |args| stat_row(args) }
  55. table table_content, header: %w[Stat Value Action]
  56. end
  57. 1 def card_stats
  58. [
  59. { title: "cards",
  60. count: Card.where(trash: false) },
  61. { title: "actions",
  62. count: Card::Action },
  63. # link_text: "clear history",
  64. # task: "clear_history" },
  65. { title: "references",
  66. count: Card::Reference }
  67. # link_text: "repair all",
  68. # task: "repair_references" }
  69. ]
  70. end
  71. 1 def cache_stats
  72. [
  73. { title: "solid cache",
  74. count: solid_cache_count, unit: " cards",
  75. link_text: "clear solid cache",
  76. task: "clear_solid_cache" },
  77. { title: "machine cache",
  78. count: machine_cache_count, unit: " cards",
  79. link_text: "clear machine cache",
  80. task: "clear_machine_cache" }
  81. ]
  82. # return stats unless Card.config.view_cache#
  83. # stats << { title: "view cache",
  84. # count: Card::View,
  85. # link_text: "clear view cache",
  86. # task: "clear_view_cache" }
  87. end
  88. 1 def memory_stats
  89. oldmem = session[:memory]
  90. session[:memory] = newmem = card.profile_memory
  91. stats = [
  92. { title: "memory now",
  93. count: newmem, unit: "M",
  94. link_text: "clear cache", task: "clear_cache" }
  95. ]
  96. return stats unless oldmem
  97. stats << { title: "memory prev", count: oldmem, unit: "M" }
  98. stats << { title: "memory diff", count: newmem - oldmem, unit: "M" }
  99. stats
  100. end
  101. 1 def stat_row args={}
  102. res = [(args[:title] || "")]
  103. res << "#{count(args[:count])}#{args[:unit]}"
  104. return res unless args[:task]
  105. res << link_to_card(:admin, (args[:link_text] || args[:task]),
  106. path: { action: :update, task: args[:task] })
  107. res
  108. end
  109. 1 def count counter
  110. counter = counter.call if counter.is_a?(Proc)
  111. counter.respond_to?(:count) ? counter.count : counter
  112. end
  113. 1 def solid_cache_count
  114. Card.search right: { codename: "solid_cache" }, return: "count"
  115. end
  116. 1 def machine_cache_count
  117. Card::Virtual.where(right_id: MachineCacheID).count
  118. end
  119. 1 def delete_sessions_link months
  120. link_to_card :admin, months, path: { action: :update, months: months,
  121. task: "delete_old_sessions" }
  122. end
  123. end
  124. 1 def current_memory_usage
  125. `ps -o rss= -p #{Process.pid}`.to_i
  126. end
  127. 1 def profile_memory &block
  128. before = current_memory_usage
  129. if block_given?
  130. instance_eval(&block)
  131. else
  132. before = 0
  133. end
  134. (current_memory_usage - before) / 1024.to_i
  135. end
  136. end;end;end;end;
  137. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/admin.rb ~~

card/tmpsets/set/mod002-admin/self/admin_info.rb

52.0% lines covered

25 relevant lines. 13 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "AdminInfo"
  4. #
  5. 1 module AdminInfo;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/admin_info.rb"; end
  8. 1 basket :warnings
  9. # For each warning in the basket (eg :my_warning), the core view
  10. # will run a test by appending a question mark (eg #my_warning?).
  11. # If it fails it will generate a message by appending message
  12. # (eg #my_warning_message).
  13. 1 add_to_basket :warnings, :no_email_delivery
  14. 1 def no_email_delivery?
  15. Card.config.action_mailer.perform_deliveries == false
  16. end
  17. 1 def clean_html?
  18. false
  19. end
  20. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  21. 1 view :core do
  22. warnings = card.warnings.map do |warning|
  23. card.send("#{warning}?") ? send("#{warning}_message") : nil
  24. end
  25. warnings.compact!
  26. warnings.empty? ? "" : warning_alert(warnings)
  27. end
  28. 1 def warning_alert warnings
  29. admin_warn = I18n.t(:admin_warn, scope: "mod.admin.set.self.admin_info")
  30. # 'ADMINISTRATOR WARNING'
  31. alert :warning, true do
  32. "<h5>#{admin_warn}</h5>" + list_tag(warnings)
  33. end
  34. end
  35. 1 def no_email_delivery_message
  36. # "Email delivery is turned off."
  37. # "Change settings in config/application.rb to send sign up notifications."
  38. I18n.t(:email_off,
  39. scope: "mod.admin.set.self.admin_info",
  40. path: "config/application.rb")
  41. end
  42. 1 def warning_list_with_auto_scope warnings
  43. # 'ADMINISTRATOR WARNING'
  44. admin_warn = tr(:admin_warn)
  45. "<h5>#{admin_warn}</h5>" + warnings.join("\n")
  46. end
  47. end
  48. end;end;end;end;
  49. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/admin_info.rb ~~

card/tmpsets/set/mod002-admin/self/debugger.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Debugger"
  4. #
  5. 1 module Debugger;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/debugger.rb"; end
  8. 1 def raw_help_text
  9. "show more useful error pages"
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/debugger.rb ~~

card/tmpsets/set/mod002-admin/self/trash.rb

48.0% lines covered

25 relevant lines. 12 lines covered and 13 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Trash"
  4. #
  5. 1 module Trash;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/trash.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :core do
  10. rows = trashed_cards.map { |tc| trash_table_row(tc) }
  11. output [
  12. restored,
  13. (empty_trash_link if rows.present?),
  14. table(rows, header: ["card", "deleted", "by", ""])
  15. ]
  16. end
  17. 1 def trashed_cards
  18. Card.where(trash: true).order(updated_at: :desc)
  19. end
  20. 1 def trash_table_row card
  21. [
  22. card.name,
  23. "#{time_ago_in_words(card.updated_at)} ago",
  24. Card[card.updater_id].name,
  25. "#{history_link(card)} | #{restore_link(card)}"
  26. ]
  27. end
  28. 1 def restored
  29. return unless (res_id = Env.params[:restore]) &&
  30. (res_card = Card[res_id.to_i])
  31. alert :success, true do
  32. wrap_with(:h5, "restored") + subformat(res_card).render_bar
  33. end
  34. end
  35. 1 def empty_trash_link
  36. wrap_with(
  37. :p,
  38. button_link("empty trash",
  39. btn_type: :default,
  40. path: { mark: :admin, action: :update, task: :empty_trash,
  41. success: { id: "~#{card.id}" } },
  42. "data-confirm" => "Are you sure you want to delete "\
  43. "all cards in the trash?")
  44. )
  45. end
  46. 1 def history_link trashed_card
  47. link_to_card trashed_card, "history",
  48. path: { view: :history, look_in_trash: true }
  49. end
  50. 1 def restore_link trashed_card
  51. before_delete = trashed_card.actions[-2]
  52. link_to "restore", method: :post,
  53. rel: "nofollow",
  54. remote: true,
  55. class: "slotter",
  56. path: { id: trashed_card.id,
  57. view: :open,
  58. look_in_trash: true,
  59. action: :update,
  60. restore: trashed_card.id,
  61. action_ids: [before_delete],
  62. success: { id: "~#{card.id}" } }
  63. end
  64. end
  65. end;end;end;end;
  66. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/trash.rb ~~

card/tmpsets/set/mod002-admin/self/version.rb

100.0% lines covered

8 relevant lines. 8 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Version"
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module Version;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/admin/set/self/version.rb"; end
  9. # require "card/version"
  10. 1 def ok_to_read
  11. 224 true
  12. end
  13. 1 def content
  14. 224 Card::Version.release
  15. end
  16. # view :core, :raw
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/admin/set/self/version.rb ~~

card/tmpsets/set/mod003-core/abstract/code_file.rb

40.0% lines covered

50 relevant lines. 20 lines covered and 30 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (CodeFile)
  4. #
  5. 1 module CodeFile;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/abstract/code_file.rb"; end
  8. 1 def self.included host_class
  9. 34 host_class.mattr_accessor :file_content_mod_name
  10. 34 host_class.file_content_mod_name = Card::Set.mod_name(caller)
  11. end
  12. # FIXME: these should abstracted and configured on the types
  13. # (same codes for `rake card:create:codefile`)
  14. # @return [Array<String>, String] the name of file(s) to be loaded
  15. 1 def source_files
  16. case type_id
  17. when CoffeeScriptID then "#{codename}.js.coffee"
  18. when JavaScriptID then "#{codename}.js"
  19. when CssID then "#{codename}.css"
  20. when ScssID then "#{codename}.scss"
  21. end
  22. end
  23. 1 def source_dir
  24. case type_id
  25. when CoffeeScriptID, JavaScriptID then "lib/javascript"
  26. when CssID, ScssID then "lib/stylesheets"
  27. else
  28. "lib"
  29. end
  30. end
  31. 1 def find_file filename
  32. modname = file_content_mod_name
  33. modname = $1 if modname =~ /^card-mod-(\w*)/
  34. mod_path = Card::Mod.dirs.path modname
  35. file_path = File.join(mod_path, source_dir, filename)
  36. unless File.exist?(file_path)
  37. Rails.logger.info "couldn't locate file #{filename} at #{file_path}"
  38. return nil
  39. end
  40. file_path
  41. end
  42. 1 def existing_source_paths
  43. Array.wrap(source_files).map do |filename|
  44. find_file(filename)
  45. end.compact
  46. end
  47. 1 def source_changed? since:
  48. existing_source_paths.any? { |path| ::File.mtime(path) > since }
  49. end
  50. 1 def content
  51. Array.wrap(source_files).map do |filename|
  52. if (source_path = find_file filename)
  53. Rails.logger.info "reading file: #{source_path}"
  54. File.read source_path
  55. end
  56. end.compact.join "\n"
  57. end
  58. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  59. 1 view :input do
  60. "Content is stored in file and can't be edited."
  61. end
  62. 1 view :file_size do
  63. "#{card.name}: #{number_to_human_size card.content.bytesize}"
  64. end
  65. 1 def short_content
  66. fa_icon("exclamation-circle", class: "text-muted pr-2") +
  67. wrap_with(:span, "file", class: "text-muted")
  68. end
  69. 1 def standard_submit_button
  70. multi_card_editor? ? super : ""
  71. end
  72. end
  73. 1 def coffee_files files
  74. files.map { |f| "script_#{f}.js.coffee" }
  75. end
  76. 1 def scss_files files
  77. files.map { |f| "style_#{f}.scss" }
  78. end
  79. end;end;end;end;
  80. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/abstract/code_file.rb ~~

card/tmpsets/set/mod003-core/abstract/haml_file.rb

58.82% lines covered

17 relevant lines. 10 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (HamlFile)
  4. #
  5. 1 module HamlFile;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/abstract/haml_file.rb"; end
  8. 1 def self.included host_class
  9. host_class.mattr_accessor :template_path
  10. host_class.extend Card::Set::Format::HamlPaths
  11. host_class.template_path = host_class.haml_template_path
  12. end
  13. 1 def content
  14. File.read template_path
  15. end
  16. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  17. 1 view :input do
  18. "Content is managed by code and cannot be edited"
  19. end
  20. 1 def haml_locals
  21. {}
  22. end
  23. 1 view :core do
  24. haml card.content, haml_locals
  25. end
  26. end
  27. end;end;end;end;
  28. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/abstract/haml_file.rb ~~

card/tmpsets/set/mod003-core/abstract/lock.rb

47.37% lines covered

19 relevant lines. 9 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Lock)
  4. #
  5. 1 module Lock;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/abstract/lock.rb"; end
  8. 1 def lock
  9. was_already_locked = locked?
  10. return if was_already_locked
  11. Auth.as_bot do
  12. lock!
  13. yield
  14. end
  15. ensure
  16. unlock! unless was_already_locked
  17. end
  18. 1 def lock_cache_key
  19. "UPDATE-LOCK:#{key}"
  20. end
  21. 1 def locked?
  22. Card.cache.read lock_cache_key
  23. end
  24. 1 def lock!
  25. Card.cache.write lock_cache_key, true
  26. end
  27. 1 def unlock!
  28. Card.cache.write lock_cache_key, false
  29. end
  30. end;end;end;end;
  31. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/abstract/lock.rb ~~

card/tmpsets/set/mod003-core/abstract/vendor_code_file.rb

90.0% lines covered

10 relevant lines. 9 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (VendorCodeFile)
  4. #
  5. 1 module VendorCodeFile;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/abstract/vendor_code_file.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 def self.included host_class
  10. 1 host_class.mattr_accessor :file_content_mod_name
  11. 1 host_class.file_content_mod_name = Card::Set.mod_name(caller)
  12. end
  13. 1 def source_dir
  14. "vendor"
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/abstract/vendor_code_file.rb ~~

card/tmpsets/set/mod003-core/all/abort.rb

79.55% lines covered

44 relevant lines. 35 lines covered and 9 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Abort)
  4. #
  5. 1 module Abort;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/abort.rb"; end
  8. # The Card#abort method is for cleanly exiting an action without continuing
  9. # to process any further events.
  10. #
  11. # Three statuses are supported:
  12. #
  13. # failure: adds an error, returns false on save
  14. # success: no error, returns true on save
  15. # triumph: similar to success, but if called on a subcard
  16. # it causes the entire action to abort (not just the subcard)
  17. 1 def abort status, msg="action canceled"
  18. 88 director.abort
  19. 88 if status == :failure && errors.empty?
  20. errors.add :abort, msg
  21. 88 elsif status.is_a?(Hash) && status[:success]
  22. success << status[:success]
  23. status = :success
  24. end
  25. 88 raise Card::Error::Abort.new(status, msg)
  26. end
  27. 1 def aborting
  28. 1 yield
  29. 1 errors.any? ? abort(:failure) : abort(:success)
  30. end
  31. 1 def abortable
  32. 2187 yield
  33. rescue Card::Error::Abort => e
  34. 88 handle_abort_error e
  35. end
  36. 1 private
  37. 1 def handle_abort_error e
  38. 88 if e.status == :triumph
  39. @supercard ? raise(e) : true
  40. 88 elsif e.status == :success
  41. 88 abort_success
  42. end
  43. end
  44. 1 def abort_success
  45. 88 if @supercard
  46. @supercard.subcards.delete key
  47. @supercard.director.subdirectors.delete self
  48. expire :soft
  49. end
  50. 88 true
  51. end
  52. # this is an override of standard rails behavior that rescues abort
  53. # makes it so that :success abortions do not rollback
  54. 1 def with_transaction_returning_status
  55. 411 status = nil
  56. 411 self.class.transaction do
  57. 411 add_to_transaction
  58. 411 remember_transaction_record_state
  59. 822 status = abortable { yield }
  60. 406 raise ActiveRecord::Rollback unless status
  61. end
  62. 406 status
  63. end
  64. # FIXME: these two do not belong here!
  65. 1 public
  66. 1 event :notable_exception_raised do
  67. error = Card::Error.current
  68. Rails.logger.debug "#{error.message}\n#{error.backtrace * "\n "}"
  69. end
  70. 1 def success
  71. 18 Env.success(name)
  72. end
  73. end;end;end;end;
  74. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/abort.rb ~~

card/tmpsets/set/mod003-core/all/actify.rb

90.7% lines covered

43 relevant lines. 39 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Actify)
  4. #
  5. 1 module Actify;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/actify.rb"; end
  8. 1 def act options={}, &block
  9. 905 if act_card
  10. 411 add_to_act options, &block
  11. else
  12. 494 start_new_act &block
  13. end
  14. end
  15. 1 def act_card
  16. 2840 Card::Director.act_card
  17. end
  18. 1 def act_card?
  19. 390 self == act_card
  20. end
  21. 1 module ClassMethods
  22. 1 def create! opts
  23. card = Card.new opts
  24. card.save!
  25. card
  26. end
  27. 1 def create opts
  28. 1 card = Card.new opts
  29. 1 card.save
  30. 1 card
  31. end
  32. end
  33. 1 def save! *args
  34. 306 as_subcard = args.first&.delete :as_subcard
  35. 612 act(as_subcard: as_subcard) { super }
  36. end
  37. 1 def save(*)
  38. 2 act { super }
  39. end
  40. 1 def valid?(*)
  41. 358 act(validating: true) { super }
  42. end
  43. 1 def update *args
  44. act { super }
  45. end
  46. 1 def update! *args
  47. 208 act { super }
  48. end
  49. 1 alias_method :update_attributes, :update
  50. 1 alias_method :update_attributes!, :update!
  51. 1 private
  52. 1 def start_new_act
  53. 494 self.director = nil
  54. 494 Director.run_act(self) do
  55. 988 run_callbacks(:act) { yield }
  56. end
  57. end
  58. 1 def add_to_act options={}
  59. 411 director.appoint self unless @director
  60. 411 director.head = true unless options[:validating] || options[:as_subcard]
  61. 411 yield
  62. end
  63. end;end;end;end;
  64. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/actify.rb ~~

card/tmpsets/set/mod003-core/all/active_card.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (ActiveCard)
  4. #
  5. 1 module ActiveCard;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/active_card.rb"; end
  8. # FIXME: -this needs a better home!
  9. 1 def format opts={}
  10. 558 opts = { format: opts.to_sym } if [Symbol, String].member? opts.class
  11. 558 Card::Format.new self, opts
  12. end
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/active_card.rb ~~

card/tmpsets/set/mod003-core/all/assign_attributes.rb

93.67% lines covered

79 relevant lines. 74 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (AssignAttributes)
  4. #
  5. 1 module AssignAttributes;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/assign_attributes.rb"; end
  8. 1 def assign_attributes args={}
  9. 4223 args = prepare_assignment_args args
  10. 4223 assign_with_subcards args do
  11. 4223 assign_with_set_modules args do
  12. 4223 params = prepare_assignment_params args
  13. 4223 super params
  14. end
  15. end
  16. end
  17. 1 def assign_set_specific_attributes
  18. 15651 set_specific.each_pair do |name, value|
  19. 63 send "#{name}=", value
  20. end
  21. end
  22. 1 def extract_subcard_args! args
  23. 4223 subcards = args.delete("subcards") || args.delete(:subcards) || {}
  24. 4223 if (subfields = args.delete("subfields") || args.delete(:subfields))
  25. subfields.each_pair do |key, value|
  26. subcards[name.field(key)] = value
  27. end
  28. end
  29. 4223 args.keys.each do |key|
  30. 5128 subcards[key] = args.delete(key) if key =~ /^\+/
  31. end
  32. 4223 subcards = subcards.to_unsafe_h if subcards.respond_to?(:to_unsafe_h)
  33. 4223 subcards
  34. end
  35. 1 protected
  36. 1 module ClassMethods
  37. 1 def assign_or_newish name, attributes, fetch_opts={}
  38. 253 if (known_card = Card.fetch(name, fetch_opts))
  39. 39 known_card.refresh.newish attributes
  40. 39 known_card
  41. else
  42. 214 Card.new attributes.merge(name: name)
  43. end
  44. end
  45. end
  46. 1 def prepare_assignment_params args
  47. 4223 args = args.to_unsafe_h if args.respond_to?(:to_unsafe_h)
  48. 4223 params = ActionController::Parameters.new(args)
  49. 4223 params.permit!
  50. 4223 params[:db_content] = standardize_content(params[:db_content]) if params[:db_content]
  51. 4223 params
  52. end
  53. 1 def prepare_assignment_args args
  54. 4223 return {} unless args
  55. 4223 args = args.stringify_keys
  56. 4223 normalize_type_attributes args
  57. 4223 stash_set_specific_attributes args
  58. 4223 args
  59. end
  60. 1 def assign_with_set_modules args
  61. 4223 set_changed = args["name"] || args["type_id"]
  62. 4223 return yield unless set_changed
  63. 8166 refresh_set_modules { yield }
  64. end
  65. 1 def assign_with_subcards args
  66. 4223 subcard_args = extract_subcard_args! args
  67. 4223 yield
  68. # name= must come before process subcards
  69. 4223 return unless subcard_args.present?
  70. 104 subcards.add subcard_args
  71. end
  72. 1 def refresh_set_modules
  73. 4083 reinclude_set_modules = @set_mods_loaded
  74. 4083 yield
  75. 4083 reset_patterns
  76. 4083 include_set_modules if reinclude_set_modules
  77. end
  78. 1 def stash_set_specific_attributes args
  79. 4223 @set_specific = {}
  80. 4223 Card.set_specific_attributes.each do |key|
  81. 38007 set_specific[key] = args.delete(key) if args.key?(key)
  82. end
  83. end
  84. 1 def normalize_type_attributes args
  85. 4223 new_type_id = extract_type_id! args unless args.delete("type_lookup") == :skip
  86. 4223 args["type_id"] = new_type_id if new_type_id
  87. end
  88. 1 def extract_type_id! args={}
  89. case
  90. 2236 when (type_id = args.delete("type_id")&.to_i)
  91. 199 type_id.zero? ? nil : type_id
  92. 2037 when (type_code = args.delete("type_code")&.to_sym)
  93. type_id_from_codename type_code
  94. 2037 when (type_name = args.delete "type")
  95. 579 type_id_from_cardname type_name
  96. end
  97. end
  98. 1 def type_id_from_codename type_code
  99. type_id_or_error(type_code) { Card::Codename.id type_code }
  100. end
  101. 1 def type_id_from_cardname type_name
  102. 1158 type_id_or_error(type_name) { Card.fetch_id type_name }
  103. end
  104. 1 def type_id_or_error val
  105. 579 type_id = yield
  106. 579 return type_id if type_id
  107. errors.add :type, "#{val} is not a known type."
  108. nil
  109. end
  110. # 'set' refers to the noun not the verb
  111. 1 def set_specific
  112. 17079 @set_specific ||= {}
  113. end
  114. end;end;end;end;
  115. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/assign_attributes.rb ~~

card/tmpsets/set/mod003-core/all/cache.rb

82.09% lines covered

67 relevant lines. 55 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Cache)
  4. #
  5. 1 module Cache;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/cache.rb"; end
  8. 1 module ClassMethods
  9. 1 def retrieve_from_cache cache_key, local_only=false
  10. 111239 return unless cache
  11. 111239 local_only ? cache.soft.read(cache_key) : cache.read(cache_key)
  12. end
  13. 1 def retrieve_from_cache_by_id id, local_only=false
  14. 30216 key = Card::Lexicon.name(id)&.key
  15. 30216 return unless key.present?
  16. 30216 retrieve_from_cache key, local_only if key
  17. end
  18. 1 def retrieve_from_cache_by_key key, local_only=false
  19. 81023 retrieve_from_cache key, local_only
  20. end
  21. 1 def write_to_cache card, local_only=false
  22. 9264 if local_only
  23. 235 write_to_soft_cache card
  24. 9029 elsif cache
  25. 9029 cache.write card.key, card
  26. end
  27. end
  28. 1 def write_to_soft_cache card
  29. 710 return unless cache
  30. 710 cache.soft.write card.key, card
  31. end
  32. 1 def expire name
  33. 43 key = name.to_name.key
  34. 43 return unless (card = Card.cache.read key)
  35. 43 card.expire
  36. end
  37. end
  38. 1 def update_soft_cache
  39. Card.write_to_soft_cache self
  40. end
  41. 1 def expire_pieces
  42. name.piece_names.each do |piece|
  43. Card.expire piece
  44. end
  45. end
  46. 1 def expire cache_type=nil
  47. 540 return unless (cache_class = cache_class_from_type cache_type)
  48. 540 expire_views
  49. 540 expire_names cache_class
  50. 540 expire_id cache_class
  51. end
  52. 1 def cache_class_from_type cache_type
  53. 540 cache_type ? Card.cache.send(cache_type) : Card.cache
  54. end
  55. 1 def view_cache_clean?
  56. !db_content_changed?
  57. end
  58. 1 def view_cache_keys
  59. 540 @view_cache_keys ||= hard_read_view_cache_keys || []
  60. end
  61. 1 def ensure_view_cache_key cache_key
  62. return if view_cache_keys.include? cache_key
  63. @view_cache_keys << cache_key
  64. hard_write_view_cache_keys
  65. end
  66. 1 def hard_read_view_cache_keys
  67. 520 Card.cache.hard&.read_attribute key, :view_cache_keys
  68. end
  69. 1 def hard_write_view_cache_keys
  70. # puts "WRITE VIEW CACHE KEYS (#{name}): #{view_cache_keys}"
  71. Card.cache.hard&.write_attribute key, :view_cache_keys, view_cache_keys
  72. end
  73. 1 def expire_views
  74. # puts "EXPIRE VIEW CACHE (#{name}): #{view_cache_keys}"
  75. 540 return unless view_cache_keys.present?
  76. Array.wrap(view_cache_keys).each do |view_cache_key|
  77. Card::View.cache.delete view_cache_key
  78. end
  79. @view_cache_keys = []
  80. hard_write_view_cache_keys
  81. end
  82. 1 def expire_names cache
  83. 540 [name, name_before_act].uniq.each do |name_version|
  84. 778 expire_name name_version, cache
  85. end
  86. end
  87. 1 def expire_name name_version, cache
  88. 778 return unless name_version.present?
  89. 545 key_version = name_version.to_name.key
  90. 545 return unless key_version.present?
  91. 545 cache.delete key_version
  92. end
  93. 1 def expire_id cache
  94. 540 return unless id.present?
  95. 389 cache.delete "~#{id}"
  96. end
  97. end;end;end;end;
  98. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/cache.rb ~~

card/tmpsets/set/mod003-core/all/chunk.rb

87.95% lines covered

83 relevant lines. 73 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Chunk)
  4. #
  5. 1 module Chunk;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/chunk.rb"; end
  8. 1 def chunks content, type, named=false
  9. 34 content ||= self.content
  10. 34 type ||= Card::Content::Chunk
  11. 34 all_chunks = Card::Content.new(content, self).find_chunks type
  12. 34 named ? all_chunks.select(&:referee_name) : all_chunks
  13. end
  14. 1 def reference_chunks content=nil, named=true
  15. chunks content, Card::Content::Chunk::Reference, named
  16. end
  17. # named=true rejects commented nests
  18. 1 def nest_chunks content=nil, named=true
  19. 34 chunks content, Card::Content::Chunk::Nest, named
  20. end
  21. # named=true rejects external links (since the don't refer to a card name)
  22. 1 def link_chunks content=nil, named=false
  23. chunks content, Card::Content::Chunk::Link, named
  24. end
  25. 1 def each_item_name_with_options content=nil
  26. reference_chunks(content).each do |chunk|
  27. options = chunk.respond_to?(:options) ? chunk.options : {}
  28. yield chunk.referee_name, options
  29. end
  30. end
  31. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  32. 1 def nest_chunks content=nil
  33. 34 content ||= _render_raw
  34. 34 card.nest_chunks content
  35. end
  36. 1 def nested_cards content=nil
  37. nest_chunks(content).map(&:referee_card).uniq
  38. end
  39. 1 def edit_fields
  40. 637 voo.edit_structure || []
  41. end
  42. 1 def nested_field_names content=nil
  43. 15 nest_chunks(content).map(&:referee_name).select { |n| field_name? n }
  44. end
  45. 1 def nested_field_cards content=nil
  46. nested_cards(content).select { |c| field_name? c.name }
  47. end
  48. 1 def field_name? name
  49. 81 name.field_of? card.name
  50. end
  51. # @return [Array] of Arrays. each is [nest_name, nest_options_hash]
  52. 1 def edit_field_configs fields_only=false
  53. 110 if edit_fields.present?
  54. 82 explicit_edit_fields_config # explicitly configured in voo or code
  55. else
  56. 28 implicit_edit_fields_config fields_only # inferred from nests
  57. end
  58. end
  59. 1 def implicit_edit_fields_config fields_only
  60. 28 result = []
  61. 28 each_nested_chunk(fields: fields_only) do |chunk|
  62. 51 result << [chunk.options[:nest_name], chunk.options]
  63. end
  64. 28 result
  65. end
  66. 1 def each_nested_field_chunk &block
  67. each_nested_chunk fields: true, &block
  68. end
  69. 1 def each_nested_chunk content: nil, fields: false, uniq: true, virtual: true, &block
  70. 28 return unless block_given?
  71. 28 chunks = prepare_nested_chunks content, fields, uniq
  72. 28 process_nested_chunks chunks, virtual, &block
  73. end
  74. 1 def uniq_chunks chunks
  75. 28 processed = ::Set.new [card.key]
  76. 28 chunks.select do |chunk|
  77. 51 key = chunk.referee_name.key
  78. 51 ok = !processed.include?(key)
  79. 51 processed << key
  80. 51 ok
  81. end
  82. end
  83. 1 def field_chunks chunks
  84. 100 chunks.select { |chunk| field_name?(chunk.referee_name) }
  85. end
  86. 1 private
  87. 1 def prepare_nested_chunks content, fields, uniq
  88. 28 chunks = nest_chunks content
  89. 28 chunks = field_chunks chunks if fields
  90. 28 chunks = uniq_chunks chunks if uniq
  91. 28 chunks
  92. end
  93. 1 def process_nested_chunks chunks, virtual, &block
  94. 28 chunks.each do |chunk|
  95. 51 process_nested_chunk chunk, virtual, &block
  96. end
  97. end
  98. 1 def process_nested_chunk chunk, virtual, &block
  99. 51 if chunk.referee_card&.virtual?
  100. process_nested_virtual_chunk chunk, &block unless virtual
  101. else
  102. 51 yield chunk
  103. end
  104. end
  105. 1 def process_virtual_chunk chunk
  106. subformat(chunk.referee_card).each_nested_field_chunk { |sub_chunk| yield sub_chunk }
  107. end
  108. 1 def explicit_edit_fields_config
  109. 82 edit_fields.map do |cardish, options|
  110. 163 field_mark = normalized_edit_field_mark cardish, options
  111. 163 options = normalized_edit_field_options options, Card::Name[field_mark]
  112. 163 [field_mark, options]
  113. end
  114. end
  115. 1 def normalized_edit_field_options options, cardname
  116. 163 options ||= cardname
  117. 163 options.is_a?(String) ? { title: options } : options
  118. end
  119. 1 def normalized_edit_field_mark cardish, options
  120. 163 return cardish if cardish.is_a?(Card) ||
  121. (options.is_a?(Hash) && options.delete(:absolute))
  122. 163 card.name.field cardish
  123. end
  124. end
  125. end;end;end;end;
  126. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/chunk.rb ~~

card/tmpsets/set/mod003-core/all/codename.rb

66.67% lines covered

21 relevant lines. 14 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Codename)
  4. #
  5. 1 module Codename;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/codename.rb"; end
  8. 1 def codename
  9. 4175 super&.to_sym
  10. end
  11. 1 event :validate_codename, :validate, on: :update, changed: :codename do
  12. validate_codename_permission
  13. validate_codename_uniqueness
  14. end
  15. 1 event :reset_codename_cache, :integrate, changed: :codename do
  16. 140 return if action == :create && codename.nil?
  17. 6 Card::Codename.reset_cache
  18. 6 Card::Codename.generate_id_constants
  19. end
  20. 1 private
  21. 1 def validate_codename_permission
  22. return if Auth.always_ok? || Auth.as_id == creator_id
  23. errors.add :codename, tr(:only_admins_codename)
  24. end
  25. 1 def validate_codename_uniqueness
  26. return (self.codename = nil) if codename.blank?
  27. return if errors.present? || !Card.find_by_codename(codename)
  28. errors.add :codename, tr(:error_code_in_use, codename: codename)
  29. end
  30. end;end;end;end;
  31. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/codename.rb ~~

card/tmpsets/set/mod003-core/all/collection.rb

63.64% lines covered

33 relevant lines. 21 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Collection)
  4. #
  5. # shared methods for card collections (Pointers, Searches, Sets, etc.)
  6. 1 module Collection;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/collection.rb"; end
  9. 1 module ClassMethods
  10. 1 def search spec, comment=nil
  11. 681 results = ::Card::Query.run(spec, comment)
  12. 681 if block_given? && results.is_a?(Array)
  13. results.each { |result| yield result }
  14. end
  15. 681 results
  16. end
  17. 1 def count_by_cql spec
  18. 6 spec = spec.clone
  19. 6 spec.delete(:offset)
  20. 6 search spec.merge(return: "count")
  21. end
  22. 1 def find_each options={}
  23. # this is a copy from rails (3.2.16) and is needed because this
  24. # is performed by a relation (ActiveRecord::Relation)
  25. find_in_batches(options) do |records|
  26. records.each { |record| yield record }
  27. end
  28. end
  29. 1 def find_in_batches options={}
  30. if block_given?
  31. super(options) do |records|
  32. yield(records)
  33. Card::Cache.reset_soft
  34. end
  35. else
  36. super(options)
  37. end
  38. end
  39. end
  40. 1 def collection?
  41. 1 item_cards != [self]
  42. end
  43. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  44. 1 view :count do
  45. card.item_names.size
  46. end
  47. end
  48. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  49. 1 view :carousel do
  50. bs_carousel unique_id, 0 do
  51. nest_item_array.each do |rendered_item|
  52. item(rendered_item)
  53. end
  54. end
  55. end
  56. end
  57. end;end;end;end;
  58. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/collection.rb ~~

card/tmpsets/set/mod003-core/all/content.rb

81.52% lines covered

92 relevant lines. 75 lines covered and 17 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Content)
  4. #
  5. 1 module Content;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/content.rb"; end
  8. 1 def content= value
  9. 391 self.db_content = standardize_content(value)
  10. end
  11. 1 def content
  12. 9884 structured_content || standard_content
  13. end
  14. 1 alias raw_content content #DEPRECATED!
  15. 1 def content?
  16. 3 content.present?
  17. end
  18. 1 def standard_content
  19. 9781 db_content || (new_card? && template.db_content)
  20. end
  21. 1 def standardize_content value
  22. 616 value.is_a?(Array) ? value.join("\n") : value
  23. end
  24. 1 def structured_content
  25. 9884 structure && template.db_content
  26. end
  27. 1 def refresh_content
  28. self.content = Card.find(id)&.db_content
  29. end
  30. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  31. 1 ONE_LINE_CHARACTER_LIMIT = 60
  32. 1 def chunk_list # override to customize by set
  33. 870 :default
  34. end
  35. 1 view :one_line_content do
  36. with_nest_mode :compact do
  37. one_line_content
  38. end
  39. end
  40. # DEPRECATED
  41. 1 view :closed_content, :one_line_content
  42. 1 view :raw_one_line_content do
  43. raw_one_line_content
  44. end
  45. 1 view :label do
  46. 42 card.label.to_s
  47. end
  48. 1 view :smart_label, cache: :never, unknown: true do
  49. 42 label_with_description render_label, label_description
  50. end
  51. 1 def label_with_description label, description
  52. 42 return label unless description
  53. "#{label} #{popover_link description}"
  54. end
  55. # TODO: move this into a nest once popovers are stub safe
  56. 1 def label_description
  57. 42 return unless (desc = card.field :description)
  58. desc.format.render_core
  59. end
  60. 1 def raw_one_line_content
  61. cut_with_ellipsis render_raw
  62. end
  63. 1 def one_line_content
  64. Card::Content.smart_truncate render_core
  65. end
  66. 1 def cut_with_ellipsis text, limit=one_line_character_limit
  67. if text.size <= limit
  68. text
  69. else
  70. text[0..(limit - 3)] + "..."
  71. end
  72. end
  73. 1 def one_line_character_limit
  74. voo.size || ONE_LINE_CHARACTER_LIMIT
  75. end
  76. end
  77. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  78. 1 view :hidden_content_field, unknown: true, cache: :never do
  79. 194 hidden_field :content, class: "d0-card-content"
  80. end
  81. end
  82. # seems like this should be moved to format so we can fall back on title
  83. # rather than name. (In fact, name, title, AND label is a bit much.
  84. # Trim to 2?)
  85. 1 def label
  86. 42 name
  87. end
  88. 1 def creator
  89. Card[creator_id]
  90. end
  91. 1 def updater
  92. 13 Card[updater_id]
  93. end
  94. 1 def save_content_draft _content
  95. clear_drafts
  96. end
  97. 1 def clear_drafts
  98. 210 drafts.created_by(Card::Auth.current_id).each(&:delete)
  99. end
  100. 1 def last_draft_content
  101. drafts.last.card_changes.last.value
  102. end
  103. 1 event :set_content, :store, on: :save do
  104. 210 self.db_content = prepare_db_content
  105. 210 @selected_action_id = @selected_content = nil
  106. 210 clear_drafts
  107. end
  108. 1 event :save_draft, :store, on: :update, when: :draft? do
  109. save_content_draft content
  110. abort :success
  111. end
  112. 1 event :set_default_content,
  113. :prepare_to_validate,
  114. on: :create, when: :use_default_content? do
  115. 39 self.db_content = template.db_content
  116. end
  117. 1 def draft?
  118. 76 Env.params["draft"] == "true"
  119. end
  120. 1 def prepare_db_content
  121. 210 cont = standard_db_content || "" # necessary?
  122. 210 clean_html? ? Card::Content.clean!(cont) : cont
  123. end
  124. 1 def standard_db_content
  125. 210 if structure
  126. # do not override db_content with content from structure
  127. 47 db_content
  128. else
  129. 163 standard_content
  130. end
  131. end
  132. 1 def clean_html?
  133. 207 true
  134. end
  135. 1 def use_default_content?
  136. 285 !db_content_changed? && template && template.db_content.present?
  137. end
  138. 1 def unfilled?
  139. 181 blank_content? && blank_comment? && !subcards?
  140. end
  141. 1 def blank_content?
  142. 181 content.blank? || content.strip.blank?
  143. end
  144. 1 def blank_comment?
  145. 26 comment.blank? || comment.strip.blank?
  146. end
  147. end;end;end;end;
  148. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/content.rb ~~

card/tmpsets/set/mod003-core/all/contextual_content.rb

100.0% lines covered

20 relevant lines. 20 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (ContextualContent)
  4. #
  5. 1 module ContextualContent;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/contextual_content.rb"; end
  8. 1 def context_card
  9. 9767 @context_card || self
  10. end
  11. 1 def with_context context_card
  12. 60 old_context = @context_card
  13. 60 @context_card = context_card if context_card
  14. 60 yield
  15. ensure
  16. 60 @context_card = old_context
  17. end
  18. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  19. 1 def context_card
  20. 5196 card.context_card
  21. end
  22. 1 def with_context context_card
  23. 60 card.with_context context_card do
  24. 60 yield
  25. end
  26. end
  27. 1 def contextual_content context_card, options={}
  28. 60 view = options.delete(:view) || :core
  29. 120 with_context(context_card) { render! view, options }
  30. end
  31. end
  32. end;end;end;end;
  33. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/contextual_content.rb ~~

card/tmpsets/set/mod003-core/all/debug.rb

52.63% lines covered

19 relevant lines. 10 lines covered and 9 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Debug)
  4. #
  5. 1 module Debug;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/debug.rb"; end
  8. 1 def to_s
  9. 21 "#<#{self.class.name}[#{debug_type}]#{attributes['name']}>"
  10. end
  11. 1 def inspect
  12. tags = []
  13. tags << "trash" if trash
  14. tags << "new" if new_card?
  15. tags << "frozen" if frozen?
  16. tags << "readonly" if readonly?
  17. tags << "virtual" if @virtual
  18. tags << "set_mods_loaded" if @set_mods_loaded
  19. error_messages = errors.any? ? "<E*#{errors.full_messages * ', '}*>" : ""
  20. "#<Card##{id}[#{debug_type}](#{name})#{error_messages}{#{tags * ','}}"
  21. end
  22. 1 private
  23. 1 def debug_type
  24. 21 "#{type_code || ''}:#{type_id}"
  25. end
  26. end;end;end;end;
  27. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/debug.rb ~~

card/tmpsets/set/mod003-core/all/event_conditions.rb

84.78% lines covered

92 relevant lines. 78 lines covered and 14 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (EventConditions)
  4. #
  5. 1 module EventConditions;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/event_conditions.rb"; end
  8. 1 Card.action_specific_attributes +=
  9. %i[skip_hash full_skip_hash trigger_hash full_trigger_hash]
  10. 1 def event_applies? event
  11. 46379 return unless set_condition_applies? event.set_module, event.opts[:changing]
  12. 15634 Card::Set::Event::CONDITIONS.all? do |key|
  13. 61714 send "#{key}_condition_applies?", event, event.opts[key]
  14. end
  15. end
  16. # force skipping this event for all cards in act
  17. 1 def skip_event! *events
  18. @full_skip_hash = nil
  19. events.each do |event|
  20. act_skip_hash[event.to_s] = :force
  21. end
  22. end
  23. # force skipping this event for this card only
  24. 1 def skip_event_in_action! *events
  25. events.each do |event|
  26. full_skip_hash[event.to_s] = :force
  27. end
  28. end
  29. # force triggering this event (when it comes up) for all cards in act
  30. 1 def trigger_event! *events
  31. 2 @full_trigger_hash = nil
  32. 2 events.each do |event|
  33. 2 act_trigger_hash[event.to_s] = :force
  34. end
  35. end
  36. # force triggering this event (when it comes up) for this card only
  37. 1 def trigger_event_in_action! *events
  38. events.each do |event|
  39. full_trigger_hash[event.to_s] = :force
  40. end
  41. end
  42. # hash form of raw skip setting, eg { "my_event" => true }
  43. 1 def skip_hash
  44. 796 @skip_hash ||= hash_with_value skip, true
  45. end
  46. 1 def trigger_hash
  47. 80 @trigger_hash ||= hash_with_value trigger, true
  48. end
  49. 1 private
  50. 1 def set_condition_applies? set_module, old_sets
  51. 46379 return true if set_module == Card
  52. 46379 set_condition_card(old_sets).singleton_class.include? set_module
  53. end
  54. 1 def on_condition_applies? _event, actions
  55. 15634 actions = Array(actions).compact
  56. 15634 actions.empty? ? true : actions.include?(action)
  57. end
  58. # if changing name/type, the old card has no-longer-applicable set modules, so we create
  59. # a new card to determine whether events apply.
  60. # (note: cached condition card would ideally be cleared after all
  61. # conditions are reviewed)
  62. # @param old_sets [True/False] whether to use the old_sets
  63. 1 def set_condition_card old_sets
  64. 46379 return self if old_sets || no_current_action?
  65. 34228 @set_condition_card ||=
  66. 362 updating_sets? ? set_condition_card_with_new_set_modules : self
  67. end
  68. # existing card is being changed in a way that alters its sets
  69. 1 def updating_sets?
  70. 362 action == :update && real? && (type_id_is_changing? || name_is_changing?)
  71. end
  72. # prevents locking in set_condition_card
  73. 1 def no_current_action?
  74. 44740 return false if @current_action
  75. 10512 @set_condition_card = nil
  76. 10512 true
  77. end
  78. 1 def set_condition_card_with_new_set_modules
  79. 1 cc = Card.find id
  80. 1 cc.name = name
  81. 1 cc.type_id = type_id
  82. 1 cc.include_set_modules
  83. end
  84. 1 def changed_condition_applies? _event, db_columns
  85. 19527 return true unless action == :update
  86. 7107 db_columns = Array(db_columns).compact
  87. 7107 return true if db_columns.empty?
  88. 3869 db_columns.any? { |col| single_changed_condition_applies? col }
  89. end
  90. 1 alias_method :changing_condition_applies?, :changed_condition_applies?
  91. 1 def when_condition_applies? _event, block
  92. 8795 case block
  93. 33 when Proc then block.call(self)
  94. 4110 when Symbol then send block
  95. 4652 else true
  96. end
  97. end
  98. # "applies always means event can run
  99. # so if skip_condition_applies?, we do NOT skip
  100. 1 def skip_condition_applies? event, allowed
  101. 8879 return true unless (val = full_skip_hash[event.name.to_s])
  102. allowed ? val.blank? : (val != :force)
  103. end
  104. 1 def trigger_condition_applies? event, required
  105. 8879 return true unless required
  106. 89 full_trigger_hash[event.name.to_s].present?
  107. end
  108. 1 def single_changed_condition_applies? db_column
  109. 2011 return true unless db_column
  110. 2011 send "#{db_column}_is_changing?"
  111. end
  112. 1 def wrong_stage opts
  113. return false if director.stage_ok? opts
  114. if !stage
  115. "phase method #{method} called outside of event phases"
  116. else
  117. "#{opts.inspect} method #{method} called in stage #{stage}"
  118. end
  119. end
  120. 1 def wrong_action actn
  121. return false if on_condition_applies?(nil, actn)
  122. "on: #{actn} method #{method} called on #{action}"
  123. end
  124. 1 def full_skip_hash
  125. 8879 @full_skip_hash ||= act_skip_hash.merge skip_in_action_hash
  126. end
  127. 1 def act_skip_hash
  128. 796 (act_card || self).skip_hash
  129. end
  130. 1 def skip_in_action_hash
  131. 796 hash_with_value skip_in_action, true
  132. end
  133. 1 def full_trigger_hash
  134. 89 @full_trigger_hash ||= act_trigger_hash.merge trigger_in_action_hash
  135. end
  136. 1 def trigger_in_action_hash
  137. 78 hash_with_value trigger_in_action, true
  138. end
  139. 1 def act_trigger_hash
  140. 80 (act_card || self).trigger_hash
  141. end
  142. 1 def hash_with_value array, value
  143. 1445 Array.wrap(array).each_with_object({}) do |event, hash|
  144. 3 hash[event.to_s] = value
  145. end
  146. end
  147. end;end;end;end;
  148. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/event_conditions.rb ~~

card/tmpsets/set/mod003-core/all/export.rb

40.0% lines covered

40 relevant lines. 16 lines covered and 24 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Export)
  4. #
  5. 1 module Export;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/export.rb"; end
  8. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  9. # returns an array of Hashes (each in export_item view)
  10. 1 view :export, cache: :never do
  11. exporting_uniques do
  12. Array.wrap(render_export_item).concat(export_items_in_view(:export)).flatten
  13. end
  14. end
  15. 1 def max_export_depth
  16. Env.params[:max_export_depth].present? ? Env.params[:max_export_depth].to_i : 2
  17. end
  18. # returns an array of Hashes (each in export_item view)
  19. 1 view :export_items, cache: :never do
  20. exporting_uniques do
  21. export_items_in_view(:export).flatten
  22. end
  23. end
  24. # returns Hash with the essentials needed to import a card into a new database
  25. 1 view :export_item do
  26. item = { name: card.name, type: card.type_name, content: card.content }
  27. item[:codename] = card.codename if card.codename
  28. track_exporting card
  29. item
  30. end
  31. 1 def export_items_in_view view
  32. within_max_depth do
  33. valid_items_for_export.map do |item|
  34. nest item, view: view
  35. end
  36. end
  37. end
  38. 1 def track_exporting card
  39. return unless @exported_keys
  40. @exported_keys << card.key
  41. end
  42. 1 def exporting_uniques
  43. @exported_keys ||= inherit(:exported_keys) || ::Set.new
  44. yield
  45. end
  46. # prevent recursion
  47. 1 def within_max_depth
  48. @export_depth ||= inherit(:export_depth).to_i + 1
  49. @export_depth > max_export_depth ? [] : yield
  50. end
  51. 1 def items_for_export
  52. nest_chunks.map do |chunk|
  53. next if chunk.try :main?
  54. chunk.referee_card
  55. end.compact
  56. end
  57. 1 def valid_items_for_export
  58. items_for_export.flatten.reject(&:blank?).uniq.find_all do |card|
  59. valid_export_card? card
  60. end
  61. end
  62. 1 def valid_export_card? ecard
  63. ecard.real? && !@exported_keys.include?(ecard.key)
  64. end
  65. end
  66. end;end;end;end;
  67. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/export.rb ~~

card/tmpsets/set/mod003-core/all/extended.rb

92.0% lines covered

25 relevant lines. 23 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Extended)
  4. #
  5. 1 module Extended;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/extended.rb"; end
  8. 1 def extended_item_cards context=nil
  9. 1 items = item_cards limit: "", context: (context || self).name
  10. 1 list = []
  11. 1 book = ::Set.new # avoid loops
  12. 1 extend_item_list items, list, book until items.empty?
  13. 1 list
  14. end
  15. 1 def extended_item_contents context=nil
  16. extended_item_cards(context).map(&:item_names).flatten
  17. end
  18. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  19. 1 delegate :extended_item_contents, to: :card
  20. end
  21. 1 private
  22. 1 def extend_item_list items, list, book
  23. 1 item = items.shift
  24. 1 return if already_extended? item, book
  25. 1 if item.collection?
  26. # keep items in order
  27. items.unshift(*item.item_cards)
  28. else # no further level of items
  29. 1 list << item
  30. end
  31. end
  32. 1 def already_extended? item, book
  33. 1 return true if book.include? item
  34. 1 book << item
  35. 1 false
  36. end
  37. # def extended_list context=nil
  38. # context = (context ? context.name : name)
  39. # args = { limit: "" }
  40. # item_cards(args.merge(context: context)).map do |x|
  41. # x.item_cards(args)
  42. # end.flatten.map do |x|
  43. # x.item_cards(args)
  44. # end.flatten.map do |y|
  45. # y.item_names(args)
  46. # end.flatten
  47. # # this could go on and on. more elegant to recurse until you don't have
  48. # # a collection
  49. # end
  50. end;end;end;end;
  51. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/extended.rb ~~

card/tmpsets/set/mod003-core/all/fetch.rb

77.55% lines covered

49 relevant lines. 38 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Fetch)
  4. #
  5. # = Card#fetch
  6. #
  7. 1 module Fetch;
  8. 1 extend Card::Set
  9. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/fetch.rb"; end
  10. # A multipurpose retrieval operator that integrates caching, database lookups,
  11. # and "virtual" card construction
  12. 1 module ClassMethods
  13. # Look for cards in
  14. # * cache
  15. # * database
  16. # * virtual cards
  17. #
  18. # @param args [Integer, String, Card::Name, Symbol, Array]
  19. # one or more of the three unique identifiers
  20. # 1. a numeric id (Integer)
  21. # 2. a name/key (String or Card::Name)
  22. # 3. a codename (Symbol)
  23. # If you pass more then one mark or an array of marks they get joined with a '+'.
  24. # The final argument can be a hash to set the following options
  25. # :skip_virtual Real cards only
  26. # :skip_modules Don't load Set modules
  27. # :look_in_trash Return trashed card objects
  28. # :local_only Use only local cache for lookup and storing
  29. # new: { opts for Card#new } Return a new card when not found
  30. # @return [Card]
  31. 1 def fetch *args
  32. 144896 Card::Fetch.new(*args)&.retrieve_or_new
  33. rescue ActiveModel::RangeError => _e
  34. return Card.new name: "card id out of range: #{f.mark}"
  35. end
  36. # fetch only real (no virtual) cards
  37. #
  38. # @param mark - see #fetch
  39. # @return [Card]
  40. 1 def [] *mark
  41. 3106 fetch(*mark, skip_virtual: true)
  42. end
  43. # fetch real cards without set modules loaded. Should only be used for simple attributes
  44. # @example
  45. # quick_fetch "A", :self, :structure
  46. #
  47. # @param mark - see #fetch
  48. # @return [Card]
  49. 1 def quick_fetch *mark
  50. 93410 fetch(*mark, skip_virtual: true, skip_modules: true)
  51. end
  52. # @return [Card]
  53. 1 def fetch_from_cast cast
  54. fetch_args = cast[:id] ? [cast[:id].to_i] : [cast[:name], { new: cast }]
  55. fetch *fetch_args
  56. end
  57. #----------------------------------------------------------------------
  58. # ATTRIBUTE FETCHING
  59. # The following methods optimize fetching of specific attributes
  60. 1 def id cardish
  61. 14367 case cardish
  62. 12658 when Integer then cardish
  63. 65 when Card then cardish.id
  64. when Symbol then Card::Codename.id cardish
  65. 1644 else fetch_id cardish
  66. end
  67. end
  68. # @param mark_parts - see #fetch
  69. # @return [Integer]
  70. 1 def fetch_id *mark_parts
  71. 74542 mark = Card::Fetch.new(*mark_parts)&.mark
  72. 74542 mark.is_a?(Integer) ? mark : quick_fetch(mark.to_s)&.id
  73. end
  74. # @param mark - see #fetch
  75. # @return [Card::Name]
  76. 1 def fetch_name *mark
  77. 6066 if (card = quick_fetch(*mark))
  78. 6061 card.name
  79. 5 elsif block_given?
  80. yield.to_name
  81. end
  82. rescue ActiveModel::RangeError => _e
  83. block_given? ? yield.to_name : nil
  84. rescue Card::Error::CodenameNotFound => e
  85. block_given? ? yield.to_name : raise(e)
  86. end
  87. # @param mark - see #fetch
  88. # @return [Integer]
  89. 1 def fetch_type_id mark
  90. 226 quick_fetch(mark)&.type_id
  91. end
  92. end
  93. #----------------------------------------------------------------------
  94. # INSTANCE METHODS
  95. # fetching from the context of a card
  96. 1 def fetch traits, opts={}
  97. 3179 opts[:new][:supercard] = self if opts[:new]
  98. 3179 Array.wrap(traits).inject(self) do |card, trait|
  99. 3179 Card.fetch card.name.trait(trait), opts
  100. end
  101. end
  102. 1 def newish opts
  103. 141 reset_patterns
  104. 141 Card.with_normalized_new_args opts do |norm_opts|
  105. 141 handle_type norm_opts do
  106. 141 assign_attributes norm_opts
  107. 141 self.name = name # trigger superize_name
  108. end
  109. end
  110. end
  111. 1 def refresh force=false
  112. 200 return self unless force || frozen? || readonly?
  113. return unless id
  114. fresh_card = self.class.find id
  115. fresh_card.include_set_modules
  116. fresh_card
  117. end
  118. end;end;end;end;
  119. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/fetch.rb ~~

card/tmpsets/set/mod003-core/all/fetch_helper.rb

100.0% lines covered

22 relevant lines. 22 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (FetchHelper)
  4. #
  5. 1 module FetchHelper;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/fetch_helper.rb"; end
  8. 1 module ClassMethods
  9. # a fetch method to support the needs of the card controller.
  10. # should be in Decko?
  11. 1 def controller_fetch args
  12. 444 card_opts = controller_fetch_opts args
  13. 444 if args[:action] == "create"
  14. # FIXME: we currently need a "new" card to catch duplicates
  15. # (otherwise save will just act like a normal update)
  16. # We may need a "#create" instance method to handle this checking?
  17. 54 Card.new card_opts
  18. else
  19. 390 standard_controller_fetch args, card_opts
  20. end
  21. end
  22. 1 private
  23. 1 def standard_controller_fetch args, card_opts
  24. 390 mark = args[:mark] || card_opts[:name]
  25. 390 card = Card.fetch mark, skip_modules: true,
  26. look_in_trash: args[:look_in_trash],
  27. new: card_opts
  28. 390 card.assign_attributes card_opts if args[:assign] && card&.real?
  29. 390 card&.include_set_modules
  30. 390 card
  31. end
  32. 1 def controller_fetch_opts args
  33. 444 opts = Env.hash args[:card]
  34. 444 opts[:type] ||= args[:type] if args[:type]
  35. # for /new/:type shortcut. we should handle in routing and deprecate this
  36. 444 opts[:name] ||= Card::Name.url_key_to_standard args[:mark]
  37. 444 opts
  38. end
  39. end
  40. end;end;end;end;
  41. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/fetch_helper.rb ~~

card/tmpsets/set/mod003-core/all/haml.rb

75.86% lines covered

29 relevant lines. 22 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Haml)
  4. #
  5. 1 module Haml;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/haml.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 include Card::Set::Format::HamlPaths
  10. 1 define_method :the_scope do
  11. set_scope
  12. end
  13. 1 define_method :haml_scope do
  14. set_scope
  15. end
  16. # Renders haml templates. The haml template can be passed as string or
  17. # block or a symbol that refers to a view template.
  18. # @param args [Hash, String, Symbol]
  19. # If a symbol is given then a template is expected in the corresponding view
  20. # directory.
  21. # @return [String] rendered haml as HTML
  22. # @example render a view template
  23. # # view/type/basic/my_template.haml:
  24. # %p
  25. # Hi
  26. # = name
  27. #
  28. # # set/type/basic.rb:
  29. # view :my_view do
  30. # haml :my_template, name: "Joe: # => "<p>Hi Joe<p/>"
  31. # end
  32. # @example use a block to pass haml
  33. # haml name: "Joe" do
  34. # <<-HAML.strip_heredoc
  35. # %p
  36. # Hi
  37. # = name
  38. # HAML
  39. # # => <p>Hi Joe</p>
  40. # @example create a slot in haml code
  41. # - haml_wrap do
  42. # %p
  43. # some haml
  44. 1 def haml *args, &block
  45. 1266 if args.first.is_a? Symbol
  46. 1266 process_haml_template(*args)
  47. else
  48. process_haml(*args, &block)
  49. end
  50. end
  51. 1 def haml_partial partial, locals={}
  52. locals[:template_path] ||= @template_path
  53. process_haml_template "_#{partial}".to_sym, locals
  54. end
  55. 1 private
  56. 1 def process_haml *args
  57. args.unshift yield if block_given?
  58. haml_to_html(*args)
  59. end
  60. 1 def process_haml_template template_name, *args
  61. 1266 locals = args.first || {}
  62. 1266 path = identify_template_path template_name, locals
  63. 1266 with_template_path path do
  64. 1266 haml_to_html ::File.read(path), *args
  65. end
  66. # rescue => e
  67. # raise Card::Error, "HAML error #{template_name}: #{e.message}\n#{e.backtrace}"
  68. end
  69. 1 def identify_template_path view, locals={}
  70. 1266 base_path = locals.delete(:template_path) || caller_locations[2].path
  71. 1266 haml_template_path view, base_path
  72. end
  73. end
  74. end;end;end;end;
  75. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/haml.rb ~~

card/tmpsets/set/mod003-core/all/i18n.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (I18n)
  4. #
  5. 1 module I18n;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/i18n.rb"; end
  8. 1 def tr key, args={}
  9. 11 ::I18n.t key, args.reverse_merge(scope: Card::Set.scope(caller))
  10. end
  11. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  12. 1 def tr key, args={}
  13. 32 ::I18n.t key, args.reverse_merge(scope: Card::Set.scope(caller))
  14. end
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/i18n.rb ~~

card/tmpsets/set/mod003-core/all/initialize.rb

100.0% lines covered

49 relevant lines. 49 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Initialize)
  4. #
  5. 1 module Initialize;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/initialize.rb"; end
  8. 1 JUNK_INIT_ARGS = %w[missing skip_virtual id].freeze
  9. 1 module ClassMethods
  10. 1 def new args={}, _options={}
  11. 3978 with_normalized_new_args args do |normalized_args|
  12. 3978 super normalized_args
  13. end
  14. end
  15. 1 def with_normalized_new_args args={}
  16. 4119 args = (args || {}).stringify_keys
  17. 4119 delete_junk_args args
  18. 4119 normalize_type_args args
  19. 4119 normalize_content_args args
  20. 4119 yield args
  21. end
  22. 1 private
  23. 1 def delete_junk_args args
  24. 16476 JUNK_INIT_ARGS.each { |a| args.delete(a) }
  25. end
  26. 1 def normalize_type_args args
  27. 12357 %w[type type_code].each { |k| args.delete(k) if args[k].blank? }
  28. end
  29. 1 def normalize_content_args args
  30. 4119 args.delete("content") if args["attach"] # should not be handled here!
  31. 4119 args["db_content"] = args.delete "content" if args["content"]
  32. end
  33. end
  34. 1 def initialize args={}
  35. 3978 args["name"] = initial_name args["name"]
  36. 3978 handle_set_modules args do
  37. 3978 handle_type args do
  38. 3978 super args # ActiveRecord #initialize
  39. end
  40. end
  41. 3978 self
  42. end
  43. 1 def handle_set_modules args
  44. 3978 skip_modules = args.delete "skip_modules"
  45. 3978 yield
  46. 3978 include_set_modules unless skip_modules
  47. end
  48. 1 def handle_type args
  49. 4119 type_lookup = args["type_lookup"]
  50. 4119 @supercard = args.delete "supercard"
  51. 4119 yield
  52. 4119 type_id_from_template if type_lookup == :force || (!type_id && type_lookup != :skip)
  53. end
  54. 1 def initial_name name
  55. 3978 name.is_a?(String) ? name : Card::Name[name].to_s
  56. end
  57. 1 def include_set_modules
  58. 24727 return self if @set_mods_loaded
  59. 15651 set_modules.each do |m|
  60. 34340 singleton_class.send :include, m
  61. end
  62. 15651 assign_set_specific_attributes
  63. 15651 @uncacheable = @set_mods_loaded = true
  64. 15651 self
  65. end
  66. 1 def uncacheable?
  67. 9220 @uncacheable == true
  68. end
  69. end;end;end;end;
  70. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/initialize.rb ~~

card/tmpsets/set/mod003-core/all/item.rb

68.83% lines covered

77 relevant lines. 53 lines covered and 24 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Item)
  4. #
  5. 1 module Item;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/item.rb"; end
  8. 1 def item_names _args={}
  9. format._render_raw.split(/[,\n]/)
  10. end
  11. 1 def item_cards _args={} # FIXME: this is inconsistent with item_names
  12. 1 [self]
  13. end
  14. 1 def item_type
  15. nil
  16. end
  17. 1 def item_keys args={}
  18. item_names(args).map do |item|
  19. item.to_name.key
  20. end
  21. end
  22. 1 def item_count args={}
  23. item_names(args).size
  24. end
  25. 1 def items_to_content array
  26. 677 items = array.map { |i| standardize_item i }.reject(&:blank?)
  27. 42 self.content = items.to_pointer_content
  28. end
  29. 1 def standardize_item item
  30. 635 Card::Name[item]
  31. end
  32. 1 def include_item? item
  33. item_names.include? Card::Name[item]
  34. end
  35. 1 def add_item item
  36. return if include_item? item
  37. items_to_content(items_strings << item)
  38. end
  39. 1 def drop_item item
  40. item = Card::Name[item]
  41. return unless include_item? item
  42. items_to_content(item_names.reject { |i| i == item })
  43. end
  44. 1 def insert_item index, name
  45. new_names = item_names
  46. new_names.delete name
  47. new_names.insert index, name
  48. items_to_content new_names
  49. end
  50. 1 def replace_item old, new
  51. return unless include_item? old
  52. drop_item old
  53. add_item new
  54. end
  55. # I think the following should work as add_item...
  56. #
  57. 1 def add_id id
  58. add_item "~#{id}"
  59. end
  60. 1 def drop_id id
  61. drop_item "~#{id}"
  62. end
  63. 1 def insert_id index, id
  64. insert_item index, "~#{id}"
  65. end
  66. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  67. 1 def item_links _args={}
  68. raw(render_core).split(/[,\n]/)
  69. end
  70. 1 def nest_item cardish, options={}, &block
  71. 56 options = item_view_options options
  72. 56 options[:nest_name] = Card::Name[cardish].s
  73. 56 nest cardish, options, &block
  74. end
  75. 1 def implicit_item_view
  76. 19 view = voo_items_view || default_item_view
  77. 19 Card::View.normalize view
  78. end
  79. 1 def voo_items_view
  80. 199 return unless voo && (items = voo.items)
  81. 199 items[:view]
  82. end
  83. 1 def default_item_view
  84. :name
  85. end
  86. 1 def item_view_options new_options={}
  87. 56 options = (voo.items || {}).clone
  88. 56 options = options.merge new_options
  89. 56 options[:view] ||= implicit_item_view
  90. 56 determine_item_view_options_type options
  91. 56 options
  92. end
  93. 1 def determine_item_view_options_type options
  94. 56 return if options[:type]
  95. 56 type_from_rule = card.item_type
  96. 56 options[:type] = type_from_rule if type_from_rule
  97. end
  98. 1 def listing listing_cards, item_args={}
  99. 16 listing_cards.map do |item_card|
  100. 19 nest_item item_card, item_args do |rendered, item_view|
  101. 19 wrap_item rendered, item_view
  102. end
  103. end
  104. end
  105. 1 def wrap_item item, _args={}
  106. item # no wrap in base
  107. end
  108. end
  109. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  110. 1 def wrap_item rendered, item_view
  111. %(<div class="item-#{item_view}">#{rendered}</div>)
  112. end
  113. end
  114. end;end;end;end;
  115. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/item.rb ~~

card/tmpsets/set/mod003-core/all/layouts.rb

64.71% lines covered

17 relevant lines. 11 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Layouts)
  4. #
  5. 1 module Layouts;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/layouts.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 attr_reader :interior
  10. 1 def layout_nest
  11. wrap_main { interior }
  12. end
  13. 1 layout :pre do # {{_main|raw}}
  14. wrap_with :pre do
  15. layout_nest
  16. end
  17. end
  18. 1 layout :simple do
  19. layout_nest
  20. end
  21. 1 layout :no_side do # {{_main|open}}
  22. <<-HTML.strip_heredoc
  23. <header>#{nest :header, view: :core}</header>
  24. <article>#{layout_nest}</article>
  25. <footer>{nest :footer, view: :core}</footer>
  26. HTML
  27. end
  28. 1 layout :default do
  29. <<-HTML.strip_heredoc
  30. <header>#{nest :header, view: :core}</header>
  31. <article>#{layout_nest}</article>
  32. <aside>#{nest :sidebar, view: :core}</aside>
  33. <footer>{nest :footer, view: :core}</footer>
  34. HTML
  35. end
  36. end
  37. end;end;end;end;
  38. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/layouts.rb ~~

card/tmpsets/set/mod003-core/all/location_history.rb

85.71% lines covered

14 relevant lines. 12 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (LocationHistory)
  4. #
  5. 1 module LocationHistory;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/location_history.rb"; end
  8. 1 event :discard_deleted_locations, :finalize, on: :delete do
  9. 6 Env.discard_locations_for self
  10. 6 configure_successful_deletion if success.target == self
  11. end
  12. 1 event :save_current_location, before: :show_page, on: :read do
  13. 315 Env.save_location self
  14. end
  15. # TO DISCUSS: should this default behavior be directly in the controller?
  16. # Or at least in decko?
  17. 1 def configure_successful_deletion
  18. 1 if Env.ajax?
  19. success.card = self
  20. success.view = :unknown unless success.view
  21. else
  22. 1 success.target = :previous
  23. end
  24. end
  25. end;end;end;end;
  26. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/location_history.rb ~~

card/tmpsets/set/mod003-core/all/name.rb

80.45% lines covered

133 relevant lines. 107 lines covered and 26 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Name)
  4. #
  5. 1 module Name;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/name.rb"; end
  8. 1 require "uuid"
  9. 1 module ClassMethods
  10. 1 def uniquify_name name, rename=:new
  11. return name unless Card.exists? name
  12. uniq_name = generate_alternative_name name
  13. return uniq_name unless rename == :old
  14. rename!(name, uniq_name)
  15. name
  16. end
  17. 1 def generate_alternative_name name
  18. uniq_name = "#{name} 1"
  19. uniq_name.next! while Card.exists?(uniq_name)
  20. uniq_name
  21. end
  22. 1 def rename! oldname, newname
  23. Card[oldname].update! name: newname, update_referers: true
  24. end
  25. end
  26. 1 def name
  27. 188027 @name ||= left_id ? Card::Lexicon.lex_to_name([left_id, right_id]) : super.to_name
  28. end
  29. 1 def key
  30. 21928 @key ||= left_id ? name.key : super
  31. end
  32. 1 def name= newname
  33. 4243 @name = superize_name newname.to_name
  34. 4243 self.key = @name.key
  35. 4243 update_subcard_names @name
  36. 4243 write_attribute :name, (@name.simple? ? @name.s : nil)
  37. 4243 assign_side_ids
  38. 4243 @name
  39. end
  40. 1 def assign_side_ids
  41. 4243 if name.simple?
  42. 807 self.left_id = self.right_id = nil
  43. else
  44. 3436 assign_side_id :left_id=, :left_name
  45. 3436 assign_side_id :right_id=, :right_name
  46. end
  47. end
  48. # assigns left_id and right_id based on names.
  49. # if side card is new, id is temporarily stored as -1
  50. 1 def assign_side_id side_id_equals, side_name
  51. 6872 side_id = Card::Lexicon.id(name.send(side_name)) || -1
  52. 6872 send side_id_equals, side_id
  53. end
  54. 1 def superize_name cardname
  55. 4243 return cardname unless @supercard
  56. 88 @raw_name = cardname.s
  57. 88 @supercard.subcards.rename key, cardname.key
  58. 88 update_superleft cardname
  59. 88 cardname.absolute_name @supercard.name
  60. end
  61. 1 def update_superleft cardname
  62. 683 @superleft = @supercard if cardname.field_of? @supercard.name
  63. end
  64. 1 def key= newkey
  65. 4243 return if newkey == key
  66. 3978 update_cache_key key do
  67. 3978 write_attribute :key, (name.simple? ? newkey : nil)
  68. 3978 @key = newkey
  69. end
  70. 3978 clean_patterns
  71. 3978 @key
  72. end
  73. 1 def clean_patterns
  74. 3978 return unless patterns?
  75. reset_patterns
  76. patterns
  77. end
  78. 1 def update_cache_key oldkey
  79. 3978 yield
  80. 3978 was_in_cache = Card.cache.soft.delete oldkey
  81. 3978 Card.write_to_soft_cache self if was_in_cache
  82. end
  83. 1 def update_subcard_names new_name, name_to_replace=nil
  84. 4243 return unless @subcards
  85. 2 subcards.each do |subcard|
  86. 2 update_subcard_name subcard, new_name, name_to_replace if subcard.new?
  87. end
  88. end
  89. 1 def update_subcard_name subcard, new_name, name_to_replace
  90. name_to_replace ||= name_to_replace_for_subcard subcard, new_name
  91. subcard.name = subcard.name.swap name_to_replace, new_name.s
  92. subcard.update_subcard_names new_name, name # needed? shouldn't #name= trigger this?
  93. end
  94. 1 def name_to_replace_for_subcard subcard, new_name
  95. # if subcard has a relative name like +C
  96. # and self is a subcard as well that changed from +B to A+B then
  97. # +C should change to A+B+C. #replace doesn't work in this case
  98. # because the old name +B is not a part of +C
  99. if subcard.name.starts_with_joint? && new_name.parts.first.present?
  100. "".to_name
  101. else
  102. name
  103. end
  104. end
  105. 1 def autoname name
  106. if Card.exists?(name) || Director.include?(name)
  107. autoname name.next
  108. else
  109. name
  110. end
  111. end
  112. # FIXME: use delegations and include all name functions
  113. 1 def simple?
  114. 14087 name.simple?
  115. end
  116. 1 def junction?
  117. 1431 name.junction?
  118. end
  119. 1 def raw_name
  120. @raw_name || name
  121. end
  122. 1 def left *args
  123. case
  124. 2834 when simple? then nil
  125. 87 when superleft then superleft
  126. when name_is_changing? && name.to_name.trunk_name == name_before_act.to_name
  127. nil # prevent recursion when, eg, renaming A+B to A+B+C
  128. else
  129. 1863 Card.fetch name.left, *args
  130. end
  131. end
  132. 1 def right *args
  133. 9780 Card.fetch(name.right, *args) unless simple?
  134. end
  135. 1 def [] *args
  136. 1008 case args[0]
  137. when Integer, Range
  138. 387 fetch_name = Array.wrap(name.parts[args[0]]).compact.join Card::Name.joint
  139. 387 Card.fetch(fetch_name, args[1] || {}) unless simple?
  140. else
  141. 621 super
  142. end
  143. end
  144. 1 def trunk *args
  145. 154 simple? ? self : left(*args)
  146. end
  147. 1 def tag *args
  148. 44 simple? ? self : Card.fetch(name.right, *args)
  149. end
  150. 1 def left_or_new args={}
  151. 239 left(args) || Card.new(args.merge(name: name.left))
  152. end
  153. # NOTE: for all these helpers, method returns *all* fields/children/descendants.
  154. # (Not just those current user has permission to read.)
  155. 1 def fields
  156. 2 field_ids.map { |id| Card[id] }
  157. end
  158. 1 def field_names
  159. field_ids.map { |id| Card::Name[id] }
  160. end
  161. 1 def field_ids
  162. 1 child_ids :left
  163. end
  164. 1 def each_child
  165. 6 child_ids.each do |id|
  166. 5 (child = Card[id]) && yield(child)
  167. # check should not be needed (remove after fixing data problems)
  168. end
  169. end
  170. # eg, A+B is a child of A and B
  171. 1 def child_ids side=nil
  172. 7 return [] unless id
  173. 7 side ||= name.simple? ? :part : :left_id
  174. 7 Auth.as_bot do
  175. 7 Card.search({ side => id, return: :id, limit: 0 }, "children of #{name}")
  176. end
  177. end
  178. 1 def each_descendant &block
  179. each_child do |child|
  180. yield child
  181. child.each_descendant(&block)
  182. end
  183. end
  184. 1 def right_id= cardish
  185. 4248 write_card_or_id :right_id, cardish
  186. end
  187. 1 def left_id= cardish
  188. 4303 write_card_or_id :left_id, cardish
  189. end
  190. 1 def write_card_or_id attribute, cardish
  191. 27686 when_id_exists(cardish) { |id| write_attribute attribute, id }
  192. end
  193. 1 def when_id_exists cardish, &block
  194. 13843 if (card_id = Card.id cardish)
  195. 12185 yield card_id
  196. 1658 elsif cardish.is_a? Card
  197. 44 with_id_after_store cardish, &block
  198. else
  199. 1614 yield cardish # eg nil
  200. end
  201. end
  202. # subcards are usually saved after super cards;
  203. # after_store forces it to save the subcard first
  204. # and callback afterwards
  205. 1 def with_id_after_store subcard
  206. 44 add_subcard subcard
  207. 88 subcard.director.after_store { |card| yield card.id }
  208. end
  209. end;end;end;end;
  210. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/name.rb ~~

card/tmpsets/set/mod003-core/all/name_events.rb

67.11% lines covered

76 relevant lines. 51 lines covered and 25 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (NameEvents)
  4. #
  5. # STAGE: prepare to validate
  6. 1 module NameEvents;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/name_events.rb"; end
  9. 1 event :set_autoname, :prepare_to_validate, on: :create do
  10. 285 if name.blank? && (autoname_card = rule_card(:autoname))
  11. self.name = autoname autoname_card.db_content
  12. # FIXME: should give placeholder in approve phase
  13. # and finalize/commit change in store phase
  14. autoname_card.refresh.update_column :db_content, name
  15. end
  16. end
  17. # STAGE: validate
  18. 1 event :validate_name, :validate, on: :save, changed: :name do
  19. 135 validate_legality_of_name
  20. 135 return if errors.any?
  21. 135 Card.write_to_soft_cache self
  22. 135 validate_uniqueness_of_name
  23. end
  24. 1 event :validate_uniqueness_of_name, skip: :allowed do
  25. # validate uniqueness of name
  26. 135 return unless (existing_id = Card::Lexicon.id key) && existing_id != id
  27. # The above is a fast check but cannot detect if card is in trash
  28. # TODO: perform the following as a remote-only fetch (not yet supported)
  29. return unless (existing_card = Card.where(id: existing_id, trash: false).take)
  30. errors.add :name, tr(:error_name_exists, name: existing_card.name)
  31. end
  32. 1 event :validate_legality_of_name do
  33. 135 if name.length > 255
  34. errors.add :name, tr(:error_too_long, length: name.length)
  35. 135 elsif name.blank?
  36. errors.add :name, tr(:error_blank_name)
  37. 135 elsif name.parts.include? ""
  38. errors.add :name, tr(:is_incomplete)
  39. 135 elsif !name.valid?
  40. errors.add :name, tr(:error_banned_characters, banned: Card::Name.banned_array * " ")
  41. 135 elsif changing_existing_tag_to_junction?
  42. errors.add :name, tr(:error_name_tag, name: name)
  43. end
  44. end
  45. 1 event :validate_key, after: :validate_name, on: :save do
  46. 135 if key.empty?
  47. errors.add :key, tr(:error_blank_key) if errors.empty?
  48. 135 elsif key != name.key
  49. errors.add :key, tr(:error_wrong_key, key: key, name: name)
  50. end
  51. end
  52. # STAGE: store
  53. 1 event :expire_old_name, :store, changed: :name, on: :update do
  54. Director.expirees << name_before_act
  55. end
  56. 1 event :update_lexicon_on_create, :finalize, changed: :name, on: :create do
  57. 134 Card::Lexicon.add self
  58. end
  59. 1 event :update_lexicon_on_rename, :finalize, changed: :name, on: :update do
  60. Card::Lexicon.update self
  61. end
  62. 1 def lex
  63. 134 simple? ? name : [left_id, right_id]
  64. end
  65. 1 def old_lex
  66. if (old_left_id = left_id_before_act)
  67. [old_left_id, right_id_before_act]
  68. else
  69. name_before_act
  70. end
  71. end
  72. 1 event :prepare_left_and_right, :store, changed: :name, on: :save do
  73. 134 return if name.simple?
  74. 101 prepare_side :left
  75. 101 prepare_side :right
  76. end
  77. 1 def prepare_side side
  78. 202 side_id = send "#{side}_id"
  79. 202 sidename = name.send "#{side}_name"
  80. 202 prepare_obstructed_side(side, side_id, sidename) ||
  81. prepare_new_side(side, side_id, sidename)
  82. end
  83. 1 def prepare_new_side side, side_id, sidename
  84. 202 return unless side_id == -1 || !Card[side_id]&.real?
  85. 65 sidecard = Director.card(sidename) || add_subcard(sidename)
  86. 65 send "#{side}_id=", sidecard
  87. end
  88. 1 def prepare_obstructed_side side, side_id, sidename
  89. 202 return unless side_id && side_id == id
  90. clear_name sidename
  91. send "#{side}_id=", add_subcard(sidename)
  92. true
  93. end
  94. 1 private
  95. 1 def changing_existing_tag_to_junction?
  96. 135 return false unless changing_name_to_junction?
  97. name_in_use_as_tag?
  98. end
  99. 1 def name_in_use_as_tag?
  100. !Card.where(right_id: id, trash: false).take.nil?
  101. end
  102. 1 def changing_name_to_junction?
  103. 135 name.junction? && simple?
  104. end
  105. 1 def old_name_in_way? sidecard
  106. real? && sidecard&.simple? && id == sidecard&.id
  107. end
  108. 1 def clear_name name
  109. # move the current card out of the way, in case the new name will require
  110. # re-creating a card with the current name, ie. A -> A+B
  111. Card.where(id: id).update_all(name: nil, key: nil, left_id: nil, right_id: nil)
  112. Card.expire name
  113. Card::Lexicon.cache.reset # probably overkill, but this for an edge case...
  114. # Card::Lexicon.delete id, key
  115. end
  116. end;end;end;end;
  117. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/name_events.rb ~~

card/tmpsets/set/mod003-core/all/observer.rb

100.0% lines covered

16 relevant lines. 16 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Observer)
  4. #
  5. 1 module Observer;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/observer.rb"; end
  8. 1 %i[create update delete].each do |action|
  9. 3 event "observer_#{action}".to_sym, :integrate, on: action do
  10. 216 execute_card_events on: action
  11. end
  12. end
  13. 1 event :cache_delete_card_events, :store, on: :delete do
  14. 6 @card_event_cache = event_cards :on_delete
  15. end
  16. 1 def execute_card_events args
  17. 216 setting = "on_#{args[:on]}".to_sym
  18. 216 event_cards(setting).each do |event_card|
  19. 1 event_card.deliver self
  20. end
  21. end
  22. 1 def event_cards setting
  23. 222 @card_event_cache ||
  24. 216 ((event_rule = rule_card(setting)) && event_rule.extended_item_cards) ||
  25. []
  26. end
  27. end;end;end;end;
  28. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/observer.rb ~~

card/tmpsets/set/mod003-core/all/pattern.rb

93.94% lines covered

33 relevant lines. 31 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Pattern)
  4. #
  5. 1 module Pattern;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/pattern.rb"; end
  8. 1 def patterns?
  9. 23161 defined? @patterns
  10. end
  11. 1 def all_patterns
  12. 105951 @all_patterns ||= set_patterns.map { |sub| sub.new self }.compact
  13. end
  14. # new cards do not
  15. 1 def patterns
  16. 14366 @patterns ||= (new_card? ? all_patterns[1..-1] : all_patterns)
  17. end
  18. 1 def reset_patterns
  19. # Rails.logger.info "resetting patterns: #{name}"
  20. 6218 @patterns = @all_patterns = nil
  21. 6218 @template = @virtual = nil
  22. 6218 @set_mods_loaded = @set_modules = @set_names = @rule_set_keys = nil
  23. 6218 @junction_only = nil # only applies to set cards
  24. 6218 true
  25. end
  26. 1 def safe_set_keys
  27. 1510 patterns.map(&:safe_key).reverse * " "
  28. end
  29. 1 def set_modules
  30. 15651 @set_modules ||= all_patterns[0..-2].reverse.map(&:module_list).flatten.compact
  31. end
  32. 1 def set_format_modules klass
  33. 14456 @set_format_modules ||= {}
  34. 14456 @set_format_modules[klass] =
  35. all_patterns[0..-2].reverse.map do |pattern|
  36. 52132 pattern.format_module_list klass
  37. end.flatten.compact
  38. end
  39. 1 def set_names
  40. 169 @set_names = patterns.map(&:to_s) if @set_names.nil?
  41. 169 @set_names
  42. end
  43. 1 def in_set? set_module
  44. patterns.map(&:module_key).include? set_module.shortname
  45. end
  46. 1 def rule_set_keys
  47. 18947 @rule_set_keys ||= patterns.map(&:rule_set_key).compact
  48. end
  49. 1 def include_module? set
  50. singleton_class&.include? set
  51. end
  52. end;end;end;end;
  53. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/pattern.rb ~~

card/tmpsets/set/mod003-core/all/permissions.rb

85.6% lines covered

125 relevant lines. 107 lines covered and 18 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Permissions)
  4. #
  5. 1 module Permissions;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/permissions.rb"; end
  8. 1 module ClassMethods
  9. 1 def repair_all_permissions
  10. Card.where("(read_rule_class is null or read_rule_id is null) and trash is false")
  11. .each do |broken_card|
  12. broken_card.include_set_modules
  13. broken_card.repair_permissions!
  14. end
  15. end
  16. end
  17. 1 def repair_permissions!
  18. rule_id, rule_class = permission_rule_id_and_class :read
  19. update_columns read_rule_id: rule_id, read_rule_class: rule_class
  20. end
  21. # ok? and ok! are public facing methods to approve one action at a time
  22. #
  23. # fetching: if the optional :trait parameter is supplied, it is passed
  24. # to fetch and the test is perfomed on the fetched card, therefore:
  25. #
  26. # trait: :account would fetch this card plus a tag codenamed :account
  27. # trait: :roles, new: {} would initialize a new card with default ({})
  28. # options.
  29. 1 def ok? action
  30. 8472 @ok ||= {}
  31. 8472 aok = @ok[Auth.as_id] ||= {}
  32. 8472 if (cached = aok[action])
  33. 2787 cached
  34. else
  35. 5685 aok[action] = send "ok_to_#{action}"
  36. end
  37. end
  38. 1 def ok! action
  39. raise Card::Error::PermissionDenied, self unless ok? action
  40. end
  41. 1 def who_can action
  42. 303 permission_rule_card(action).item_cards.map(&:id)
  43. end
  44. 1 def anyone_can? action
  45. who_can(action).include? AnyoneID
  46. end
  47. 1 def direct_rule_card action
  48. 136 direct_rule_id = rule_card_id action
  49. 136 require_permission_rule! direct_rule_id, action
  50. 136 Card.quick_fetch direct_rule_id
  51. end
  52. 1 def permission_rule_id action
  53. 675 if junction? && rule(action).match?(/^\[?\[?_left\]?\]?$/)
  54. 235 left_permission_rule_id action
  55. else
  56. 440 rule_card_id(action)
  57. end
  58. end
  59. 1 def permission_rule_id_and_class action
  60. 136 [permission_rule_id(action), direct_rule_card(action).rule_class_name]
  61. end
  62. 1 def left_permission_rule_id action
  63. 239 lcard = left_or_new(skip_virtual: true, skip_modules: true)
  64. 239 if action == :create && lcard.real? && lcard.action != :create
  65. 18 action = :update
  66. end
  67. 239 lcard.permission_rule_id action
  68. end
  69. 1 def permission_rule_card action
  70. 303 Card.fetch permission_rule_id(action)
  71. end
  72. 1 def require_permission_rule! rule_id, action
  73. 136 return if rule_id
  74. # RULE missing. should not be possible.
  75. # generalize this to handling of all required rules
  76. errors.add :permission_denied, tr(:error_no_action_rule, action: action, name: name)
  77. raise Card::Error::PermissionDenied, self
  78. end
  79. 1 def rule_class_name
  80. 136 trunk.type_id == SetID ? name.trunk_name.tag : nil
  81. end
  82. 1 def you_cant what
  83. 17 "You don't have permission to #{what}"
  84. end
  85. 1 def deny_because why
  86. 17 @permission_errors << why if @permission_errors
  87. 17 false
  88. end
  89. 1 def permitted? action
  90. 1200 return false if Card.config.read_only # :read does not call #permit
  91. 1200 return true if Auth.always_ok?
  92. 298 Auth.as_card.among? who_can(action)
  93. end
  94. 1 def permit action, verb=nil
  95. # not called by ok_to_read
  96. 1192 if Card.config.read_only
  97. deny_because "Currently in read-only mode"
  98. return false
  99. end
  100. 1192 return true if permitted? action
  101. 17 verb ||= action.to_s
  102. 17 deny_because you_cant("#{verb} #{name.present? ? name : 'this'}")
  103. end
  104. 1 def ok_to_create
  105. 845 return false unless permit :create
  106. 840 return true if simple?
  107. 181 %i[left right].each do |side|
  108. # left is supercard; create permissions will get checked there.
  109. 362 next if side == :left && superleft
  110. 275 part_card = send side, new: {}
  111. # if no card, there must be other errors
  112. 275 next unless part_card && part_card.new_card?
  113. 58 unless part_card.ok? :create
  114. deny_because you_cant("create #{part_card.name}")
  115. return false
  116. end
  117. end
  118. 181 true
  119. end
  120. 1 def ok_to_read
  121. 3699 return true if Auth.always_ok?
  122. 1647 self.read_rule_id ||= permission_rule_id :read
  123. 1647 return true if Auth.as_card.read_rules_hash[read_rule_id]
  124. deny_because you_cant "read this"
  125. end
  126. 1 def ok_to_update
  127. 336 return false unless permit(:update)
  128. 327 return true unless type_id_changed? && !permitted?(:create)
  129. deny_because you_cant("change to this type (need create permission)")
  130. end
  131. 1 def ok_to_delete
  132. 11 permit :delete
  133. end
  134. # don't know why we introduced this
  135. # but we have to preserve read rules to make
  136. # delete acts visible in recent changes -pk
  137. # event :clear_read_rule, :store, on: :delete do
  138. # self.read_rule_id = self.read_rule_class = nil
  139. # end
  140. 1 event :set_read_rule, :store,
  141. on: :save, changed: %i[type_id name] do
  142. 135 read_rule_id, read_rule_class = permission_rule_id_and_class(:read)
  143. 135 self.read_rule_id = read_rule_id
  144. 135 self.read_rule_class = read_rule_class
  145. end
  146. 1 event :set_field_read_rules,
  147. after: :set_read_rule, on: :update, changed: :type_id do
  148. # find all cards with me as trunk and update their read_rule
  149. # (because of *type plus right)
  150. # skip if name is updated because will already be resaved
  151. 1 each_field_as_bot do |field|
  152. 1 field.refresh.update_read_rule
  153. end
  154. end
  155. 1 def update_field_read_rules
  156. 1 return unless type_id_changed? || read_rule_id_changed?
  157. each_field_as_bot do |field|
  158. field.update_read_rule if field.rule(:read) == "_left"
  159. end
  160. end
  161. 1 def each_field_as_bot
  162. 1 Auth.as_bot do
  163. 2 fields.each { |field| yield field }
  164. end
  165. end
  166. 1 def without_timestamps
  167. 1 Card.record_timestamps = false
  168. 1 yield
  169. ensure
  170. 1 Card.record_timestamps = true
  171. end
  172. 1 event :update_read_rule do
  173. 1 without_timestamps do
  174. 1 reset_patterns # why is this needed?
  175. 1 rcard_id, rclass = permission_rule_id_and_class :read
  176. # these two are just to make sure vals are correct on current object
  177. 1 self.read_rule_id = rcard_id
  178. 1 self.read_rule_class = rclass
  179. 1 Card.where(id: id).update_all read_rule_id: rcard_id,
  180. read_rule_class: rclass
  181. 1 expire :hard
  182. 1 update_field_read_rules
  183. end
  184. end
  185. 1 def add_to_read_rule_update_queue updates
  186. @read_rule_update_queue = Array.wrap(@read_rule_update_queue).concat updates
  187. end
  188. 1 event :check_permissions, :validate do
  189. 297 track_permission_errors do
  190. 297 ok? action_for_permission_check
  191. end
  192. end
  193. 1 def action_for_permission_check
  194. 297 commenting? ? :update : action
  195. end
  196. 1 def track_permission_errors
  197. 297 @permission_errors = []
  198. 297 result = yield
  199. 300 @permission_errors.each { |msg| errors.add :permission_denied, msg }
  200. 297 @permission_errors = nil
  201. 297 result
  202. end
  203. end;end;end;end;
  204. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/permissions.rb ~~

card/tmpsets/set/mod003-core/all/references.rb

78.49% lines covered

93 relevant lines. 73 lines covered and 20 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (References)
  4. #
  5. # frozen_string_literal: true
  6. # Cards can refer to other cards in their content, eg via links and nests.
  7. 1 module References;
  8. 1 extend Card::Set
  9. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/references.rb"; end
  10. # The card that refers is the "referer", the card that is referred to is
  11. # the "referee". The reference itself has its own class (Card::Reference),
  12. # which handles id-based reference tracking.
  13. 1 PARTIAL_REF_CODE = "P".freeze
  14. # cards that refer to self
  15. 1 def referers
  16. 76 referer_cards_from_references references_in
  17. end
  18. # cards that include self
  19. 1 def nesters
  20. referer_cards_from_references references_in.where(ref_type: "I")
  21. end
  22. 1 def referer_cards_from_references references
  23. 76 references.map(&:referer_id).uniq.map(&Card.method(:fetch)).compact
  24. end
  25. # cards that self refers to
  26. 1 def referees
  27. referees_from_references references_out
  28. end
  29. # cards that self includes
  30. 1 def nestees
  31. referees_from_references references_out.where(ref_type: "I")
  32. end
  33. 1 def referees_from_references references
  34. references.map(&:referee_key).uniq.map { |key| Card.fetch key, new: {} }
  35. end
  36. # cards that refer to self by name
  37. # (finds cards not yet linked by id)
  38. 1 def name_referers
  39. Card.joins(:references_out).where card_references: { referee_key: key }
  40. end
  41. # replace references in card content
  42. 1 def replace_reference_syntax old_name, new_name
  43. obj_content = Card::Content.new content, self
  44. obj_content.find_chunks(Card::Content::Chunk::Reference).select do |chunk|
  45. next unless (old_ref_name = chunk.referee_name)
  46. next unless (new_ref_name = old_ref_name.swap old_name, new_name)
  47. chunk.referee_name = chunk.replace_reference old_name, new_name
  48. refs = Card::Reference.where referee_key: old_ref_name.key
  49. refs.update_all referee_key: new_ref_name.key
  50. end
  51. obj_content.to_s
  52. end
  53. # delete old references from this card's content, create new ones
  54. 1 def update_references_out
  55. 260 delete_references_out
  56. 260 create_references_out
  57. end
  58. # interpret references from this card's content and
  59. # insert entries in reference table
  60. 1 def create_references_out
  61. 260 ref_hash = {}
  62. 260 each_reference_out do |referee_name, ref_type|
  63. 675 interpret_reference ref_hash, referee_name, ref_type
  64. end
  65. 260 return if ref_hash.empty?
  66. 179 Card::Reference.mass_insert reference_values_array(ref_hash)
  67. end
  68. # delete references from this card
  69. 1 def delete_references_out
  70. 266 raise "id required to delete references" if id.nil?
  71. 266 Card::Reference.where(referer_id: id).delete_all
  72. end
  73. # interpretation phase helps to prevent duplicate references
  74. # results in hash like:
  75. # { referee1_key: [referee1_id, referee1_type2],
  76. # referee2_key...
  77. # }
  78. 1 def interpret_reference ref_hash, raw_referee_name, ref_type
  79. 833 with_normalized_referee raw_referee_name do |referee_name, referee_key, referee_id|
  80. 747 ref_hash[referee_key] ||= [referee_id]
  81. 747 ref_hash[referee_key] << ref_type
  82. 747 interpret_partial_references ref_hash, referee_name unless referee_id
  83. end
  84. end
  85. # Partial references are needed to track references to virtual cards.
  86. # For example a link to virual card [[A+*self]] won't have a referee_id,
  87. # but when A's name is changed we have to find and update that link.
  88. 1 def interpret_partial_references ref_hash, referee_name
  89. 99 return if referee_name.simple?
  90. 79 [referee_name.left, referee_name.right].each do |sidename|
  91. 158 interpret_reference ref_hash, sidename, PARTIAL_REF_CODE
  92. end
  93. end
  94. # translate interpreted reference hash into values array,
  95. # removing duplicate and unnecessary ref_types
  96. 1 def reference_values_array ref_hash
  97. 179 values = []
  98. 179 ref_hash.each do |referee_key, hash_val|
  99. 747 referee_id = hash_val.shift || "null"
  100. 747 ref_types = hash_val.uniq
  101. 747 ref_types.delete PARTIAL_REF_CODE if ref_types.size > 1
  102. # partial references are not necessary if there are explicit references
  103. 747 ref_types.each do |ref_type|
  104. 747 values << [id, referee_id, "'#{referee_key}'", "'#{ref_type}'"]
  105. end
  106. end
  107. 179 values
  108. end
  109. # invokes the given block for each reference in content with
  110. # the reference name and reference type
  111. 1 def each_reference_out
  112. 207 content_object.find_chunks(Card::Content::Chunk::Reference).each do |chunk|
  113. 200 yield chunk.referee_name, chunk.reference_code
  114. end
  115. end
  116. 1 def has_nests?
  117. content_object.has_chunk? Card::Content::Chunk::Nest
  118. end
  119. 1 def content_object
  120. 207 Card::Content.new content, self
  121. end
  122. 1 protected
  123. # test for updating referer content
  124. 1 event :prepare_referer_update, :validate, on: :update, changed: :name do
  125. self.update_referers = ![nil, false, "false"].member?(update_referers)
  126. end
  127. # on rename, update names in cards that refer to self by name (as directed)
  128. 1 event :update_referer_content, :finalize, on: :update, when: :update_referers do
  129. referers.each do |card|
  130. next if card.structure
  131. card.skip_event! :validate_renaming, :check_permissions
  132. card.content = card.replace_reference_syntax name_before_act, name
  133. attach_subcard card
  134. end
  135. end
  136. # on rename, when NOT updating referer content, update references to ensure
  137. # that partial references are correctly tracked
  138. # eg. A links to X+Y. if X+Y is renamed and we're not updating the link in A,
  139. # then we need to be sure that A has a partial reference
  140. 1 event :update_referer_references_out, :finalize,
  141. on: :update, when: :not_update_referers do
  142. 76 referers.map(&:update_references_out)
  143. end
  144. # when name changes, update references to card
  145. 1 event :refresh_references_in, :finalize, on: :save do
  146. 210 Card::Reference.unmap_referees id if action == :update && !update_referers
  147. 210 Card::Reference.map_referees key, id
  148. end
  149. # when content changes, update references to other cards
  150. 1 event :refresh_references_out, :finalize, on: :save, changed: :content do
  151. 182 update_references_out
  152. end
  153. # clean up reference table when card is deleted
  154. 1 event :clear_references, :finalize, on: :delete do
  155. 6 delete_references_out
  156. 6 Card::Reference.unmap_referees id
  157. end
  158. 1 def not_update_referers
  159. 76 !update_referers
  160. end
  161. 1 private
  162. 1 def with_normalized_referee referee_name
  163. 833 return unless referee_name # eg commented nest has no referee_name
  164. 833 referee_name = referee_name.to_name
  165. 833 referee_key = referee_name.key
  166. 833 return if referee_key == key # don't create self reference
  167. 747 yield referee_name, referee_key, Card::Lexicon.id(referee_name)
  168. end
  169. end;end;end;end;
  170. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/references.rb ~~

card/tmpsets/set/mod003-core/all/rename.rb

33.33% lines covered

27 relevant lines. 9 lines covered and 18 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Rename)
  4. #
  5. 1 module Rename;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/rename.rb"; end
  8. 1 event :rename_in_trash, after: :expire_old_name, on: :update do
  9. existing_card = Card.find_by_key_and_trash name.key, true
  10. return if !existing_card || existing_card == self
  11. existing_card.name = existing_card.name + "*trash"
  12. existing_card.rename_in_trash_without_callbacks
  13. existing_card.save!
  14. end
  15. 1 event :validate_renaming, :validate, on: :update, changed: :name, skip: :allowed do
  16. return if name_before_act&.to_name == name # just changing to new variant
  17. errors.add :content, tr(:cannot_change_content) if content_is_changing?
  18. errors.add :type, tr(:cannot_change_type) if type_is_changing?
  19. detect_illegal_compound_names
  20. end
  21. 1 event :cascade_name_changes, :finalize, on: :update, changed: :name do
  22. each_descendant do |d|
  23. d.action = :update
  24. update_referers ? d.update_referers : d.update_referer_references_out
  25. d.refresh_references_in
  26. d.refresh_references_out
  27. d.expire
  28. end
  29. end
  30. 1 def changed_from_simple_to_compound?
  31. name.compound? && name_before_act.to_name.simple?
  32. end
  33. 1 def detect_illegal_compound_names
  34. return unless changed_from_simple_to_compound? && child_ids(:right).present?
  35. errors.add :name, "illegal name change; existing names end in +#{name_before_act}"
  36. end
  37. end;end;end;end;
  38. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/rename.rb ~~

card/tmpsets/set/mod003-core/all/rules.rb

82.61% lines covered

46 relevant lines. 38 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Rules)
  4. #
  5. 1 module Rules;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/rules.rb"; end
  8. 1 def rule setting_code
  9. 736 rule_card(setting_code, skip_modules: true)&.db_content
  10. end
  11. 1 def rule_card setting_code, options={}
  12. 18006 Card.fetch rule_card_id(setting_code), options
  13. end
  14. 1 def rule_card_id setting_code
  15. 18582 rule_id_lookup Card::Rule.rule_cache, setting_code
  16. end
  17. 1 def preference setting_code, user=nil
  18. 365 preference_card(setting_code, user, skip_modules: true)&.db_content
  19. end
  20. 1 def preference_card setting_code, user=nil, options={}
  21. 365 Card.fetch preference_card_id(setting_code, user), options
  22. end
  23. 1 def preference_card_id setting_code, user=nil
  24. 365 return unless (user_id = preference_user_id user)
  25. 365 rule_id_lookup Card::Rule.preference_cache,
  26. "#{setting_code}+#{user_id}",
  27. "#{setting_code}+#{AllID}"
  28. end
  29. 1 def is_rule?
  30. 7890 is_standard_rule? || is_preference?
  31. end
  32. 1 def is_standard_rule?
  33. 7890 (r = right(skip_modules: true)) &&
  34. r.type_id == SettingID &&
  35. 697 (l = left(skip_modules: true)) &&
  36. l.type_id == SetID
  37. end
  38. 1 def is_preference?
  39. 7459 name.parts.length > 2 &&
  40. 1218 (r = right(skip_modules: true)) &&
  41. r.type_id == SettingID &&
  42. 184 (set = self[0..-3, skip_modules: true]) &&
  43. set.type_id == SetID &&
  44. 182 (user = self[-2, skip_modules: true]) &&
  45. 182 (user.type_id == UserID || user.codename == :all)
  46. end
  47. # FIXME: move to a better place (if still needed) and use codenames
  48. 1 def related_sets with_self=false
  49. # refers to sets that users may configure from the current card -
  50. # NOT to sets to which the current card belongs
  51. sets = []
  52. sets << ["#{name}+*self", Card::Set::Self.label(name)] if with_self
  53. if known? && name.simple?
  54. sets << ["#{name}+*right", Card::Set::Right.label(name)]
  55. end
  56. sets
  57. end
  58. 1 private
  59. 1 def preference_user_id user
  60. 365 case user
  61. 365 when Integer then user;
  62. when Card then user
  63. when nil then Auth.current_id
  64. else
  65. raise Card::ServerError, "invalid preference user"
  66. end
  67. end
  68. 1 def rule_id_lookup lookup_hash, cache_suffix, fallback_suffix=nil
  69. 18947 rule_set_keys.each do |rule_set_key|
  70. 76482 rule_id = lookup_hash["#{rule_set_key}+#{cache_suffix}"]
  71. 76482 rule_id ||= fallback_suffix && lookup_hash["#{rule_set_key}+#{fallback_suffix}"]
  72. 76482 return rule_id if rule_id
  73. end
  74. nil
  75. end
  76. end;end;end;end;
  77. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/rules.rb ~~

card/tmpsets/set/mod003-core/all/states.rb

85.71% lines covered

35 relevant lines. 30 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (States)
  4. #
  5. # All cards have one (and only one) of these three states: real, virtual, and unknown.
  6. #
  7. # - *real* cards are stored in the database (but not in the trash) and have a unique id.
  8. # - *virtual* cards are not real, but they act real based on rules. For example,
  9. # Home+*editors does a search for all the users who have edited the "Home" card.
  10. # There are many other similar cards that search for things like references, children,
  11. # etc. But we don't store all these cards in the database; we generate them dynamically
  12. 1 module States;
  13. 1 extend Card::Set
  14. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/states.rb"; end
  15. # based on the names.
  16. # - *unknown* cards are everything else.
  17. #
  18. # These states are frequently grouped as follows:
  19. #
  20. # - *known* cards are either _real_ or _virtual_
  21. # - *new* (or *unreal*) cards are either _unknown_ or _virtual_
  22. 1 module ClassMethods
  23. 1 def real? mark
  24. 412 quick_fetch(mark).present?
  25. end
  26. 1 alias exist? real?
  27. 1 alias exists? real?
  28. 1 def known? mark
  29. 2623 fetch(mark).present?
  30. end
  31. end
  32. # @return [Symbol] :real, :virtual, or :unknown
  33. 1 def state anti_fishing=true
  34. case
  35. 12 when !known? then :unknown
  36. when anti_fishing && !ok?(:read) then :unknown
  37. 4 when real? then :real
  38. when virtual? then :virtual
  39. else :wtf
  40. end
  41. end
  42. # @return [True/False]
  43. 1 def real?
  44. 11165 !unreal?
  45. end
  46. # Virtual cards are structured, compound cards that are not stored in the database. You
  47. # can create virtual cards with structure rules.
  48. #
  49. # Some cards with hard-coded content will also override the #virtual? method. This
  50. # is established practice, but it is NOT advisable to override any of the other
  51. # state methods.
  52. #
  53. # @return [True/False]
  54. 1 def virtual?
  55. 1295 if @virtual.nil?
  56. 876 @virtual = real? || name.simple? ? false : structure.present?
  57. end
  58. 1295 @virtual
  59. end
  60. # @return [True/False]
  61. 1 def unknown?
  62. 578 !known?
  63. end
  64. # @return [True/False]
  65. 1 def known?
  66. 9269 real? || virtual?
  67. end
  68. # @return [True/False]
  69. 1 def new?
  70. 251794 new_record? || # not yet in db (from ActiveRecord)
  71. !@from_trash.nil? # in process of restoration from trash
  72. end
  73. 1 alias new_card? new?
  74. 1 alias unreal? new?
  75. # has not been edited directly by human users. bleep blorp.
  76. 1 def pristine?
  77. new_card? || !user_changes?
  78. end
  79. 1 def user_changes?
  80. actions.joins(:act).where("card_acts.actor_id != ?", WagnBotID).exists?
  81. end
  82. end;end;end;end;
  83. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/states.rb ~~

card/tmpsets/set/mod003-core/all/subcards.rb

68.66% lines covered

67 relevant lines. 46 lines covered and 21 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Subcards)
  4. #
  5. 1 module Subcards;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/subcards.rb"; end
  8. 1 def field tag, opts={}
  9. 45 Card.fetch name.field(tag), opts
  10. end
  11. 1 def subcard card_name
  12. subcards.card card_name
  13. end
  14. 1 def subfield field_name
  15. 147 subcards.field field_name
  16. end
  17. 1 def field? tag
  18. 3 field(tag) || subfield(tag)
  19. end
  20. 1 def subcards
  21. 4654 @subcards ||= Card::Subcards.new self
  22. end
  23. 1 def subcards?
  24. 26 subcards.present?
  25. end
  26. 1 def expire_subcards
  27. subcards.clear
  28. end
  29. # phase_method :attach_subcard, before: :store do |name_or_card, args=nil|
  30. # TODO: handle differently in different stages
  31. 1 def add_subcard name_or_card, args={}
  32. 132 subcards.add name_or_card, args
  33. end
  34. 1 alias_method :attach_subcard, :add_subcard
  35. 1 def add_subcard! name_or_card, args={}
  36. subcard = subcards.add name_or_card, args
  37. subcard.director.reset_stage
  38. subcard
  39. end
  40. 1 alias_method :attach_subcard!, :add_subcard!
  41. # phase_method :attach_subfield, before: :approve do |name_or_card, args=nil|
  42. 1 def attach_subfield name_or_card, args={}
  43. 7 subcards.add_field name_or_card, args
  44. end
  45. 1 alias_method :add_subfield, :attach_subfield
  46. 1 def attach_subfield! name_or_card, args={}
  47. subcard = subcards.add_field name_or_card, args
  48. subcard.director.reset_stage
  49. subcard
  50. end
  51. 1 def detach_subcard name_or_card
  52. 24 subcards.remove name_or_card
  53. end
  54. 1 alias_method :remove_subcard, :detach_subcard
  55. 1 def detach_subfield name_or_card
  56. subcards.remove_field name_or_card
  57. end
  58. 1 alias_method :remove_subfield, :detach_subfield
  59. 1 def clear_subcards
  60. subcards.clear
  61. end
  62. # ensures subfield is present
  63. # does NOT override subfield content if already present
  64. 1 def ensure_subfield field_name, args={}
  65. if subfield_present? field_name
  66. subfield field_name
  67. else
  68. add_subfield field_name, args
  69. end
  70. end
  71. 1 def subfield_present? field_name
  72. subfield(field_name)&.content&.present?
  73. end
  74. 1 def deep_clear_subcards
  75. subcards.deep_clear
  76. end
  77. 1 def handle_subcard_errors
  78. 2904 subcards.each do |subcard|
  79. 1224 subcard.errors.each do |field, err|
  80. subcard_error subcard, field, err
  81. end
  82. 1224 subcard.errors.clear
  83. end
  84. end
  85. 1 def subcard_error subcard, field, err
  86. err = "#{field} #{err}" unless %i[content abort].member? field
  87. errors.add subcard.name.from(name), err
  88. end
  89. 1 event :reject_empty_subcards, :prepare_to_validate do
  90. 451 subcards.each_with_key do |subcard, key|
  91. 201 next unless subcard.new? && subcard.unfilled?
  92. 24 remove_subcard(key)
  93. 24 director.subdirectors.delete(subcard)
  94. end
  95. end
  96. # check when deleting field that left has not also been deleted
  97. 1 def trashed_left?
  98. l = left
  99. !l || l.trash
  100. end
  101. # check when renaming field that it is not actually the same field
  102. # (eg on a renamed trunk)
  103. 1 def same_field?
  104. (left_id == left_id_before_act) && (right_id == right_id_before_act)
  105. end
  106. end;end;end;end;
  107. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/subcards.rb ~~

card/tmpsets/set/mod003-core/all/tabs.rb

40.0% lines covered

30 relevant lines. 12 lines covered and 18 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Tabs)
  4. #
  5. 1 module Tabs;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/tabs.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :tabs do
  10. construct_tabs "tabs"
  11. end
  12. 1 def construct_tabs tab_type
  13. tabs = { active: {}, paths: {} }
  14. voo.items[:view] ||= :content
  15. card.each_item_name_with_options(_render_raw) do |name, options|
  16. construct_tab tabs, name, options
  17. end
  18. tabs tabs[:paths], tabs[:active][:name], tab_type: tab_type, load: :lazy do
  19. tabs[:active][:content]
  20. end
  21. end
  22. 1 def construct_tab tabs, name, explicit_options
  23. tab_options = item_view_options explicit_options
  24. tabs[:paths][name] = {
  25. title: nest(name, view: :title, title: tab_options[:title]),
  26. path: nest_path(name, tab_options).html_safe
  27. }
  28. return unless tabs[:active].empty?
  29. tabs[:active] = { name: name, content: nest(name, tab_options) }
  30. end
  31. # def tab_title title, name
  32. # return name unless title
  33. # name.to_name.title title, @context_names
  34. # end
  35. 1 view :pills do
  36. construct_tabs "pills"
  37. end
  38. 1 view :tabs_static do
  39. construct_static_tabs "tabs"
  40. end
  41. 1 view :pills_static do
  42. construct_static_tabs "pills"
  43. end
  44. 1 def construct_static_tabs tab_type
  45. tabs = {}
  46. card.item_cards.each do |item|
  47. tabs[item.name] = nest item, item_view_options(args)
  48. end
  49. tabs tabs, nil, tab_type: tab_type
  50. end
  51. end
  52. end;end;end;end;
  53. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/tabs.rb ~~

card/tmpsets/set/mod003-core/all/templating.rb

95.0% lines covered

40 relevant lines. 38 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Templating)
  4. #
  5. 1 module Templating;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/templating.rb"; end
  8. 1 def is_template?
  9. return @is_template unless @is_template.nil?
  10. @is_template = name.trait_name? :structure, :default
  11. end
  12. 1 def is_structure?
  13. 3229 return @is_structure unless @is_structure.nil?
  14. 1400 @is_structure = name.trait_name? :structure
  15. end
  16. 1 def template
  17. # currently applicable templating card.
  18. # note that a *default template is never returned for an existing card.
  19. 17423 @template ||= begin
  20. 11688 @virtual = false
  21. 11688 if new_card?
  22. 2818 new_card_template
  23. else
  24. 8870 structure_rule_card
  25. end
  26. end
  27. end
  28. 1 def default_type_id
  29. 62 Card.default_type_id
  30. end
  31. 1 def new_card_template
  32. 2818 default = rule_card :default, skip_modules: true
  33. 2818 dup_card = dup
  34. 2818 dup_card.type_id = default&.type_id || default_type_id
  35. 2818 if (structure = dup_card.structure_rule_card)
  36. 744 @virtual = true if junction?
  37. 744 self.type_id = structure.type_id if assign_type_to?(structure)
  38. 744 structure
  39. else
  40. 2074 default
  41. end
  42. end
  43. 1 def assign_type_to? structure
  44. 744 return if type_id == structure.type_id
  45. 430 structure.assigns_type?
  46. end
  47. 1 def assigns_type?
  48. # needed because not all *structure templates govern the type of set members
  49. # for example, X+*type+*structure governs all cards of type X,
  50. # but the content rule does not (in fact cannot) have the type X.
  51. 477 pattern_code = Card.quick_fetch(name.trunk_name.tag_name)&.codename
  52. 477 return unless pattern_code && (set_class = Set::Pattern.find pattern_code)
  53. 477 set_class.assigns_type
  54. end
  55. 1 def structure
  56. 11750 return unless template && template.is_structure?
  57. 676 template
  58. end
  59. 1 def structure_rule_card
  60. 11688 return unless (card = rule_card :structure, skip_modules: true)
  61. 877 card.db_content&.strip == "_self" ? nil : card
  62. end
  63. end;end;end;end;
  64. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/templating.rb ~~

card/tmpsets/set/mod003-core/all/trash.rb

55.07% lines covered

69 relevant lines. 38 lines covered and 31 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Trash)
  4. #
  5. 1 module Trash;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/trash.rb"; end
  8. 1 Self::Admin.add_to_basket(
  9. :tasks,
  10. name: :empty_trash,
  11. irreversible: true,
  12. execute_policy: -> { Card.empty_trash },
  13. stats: {
  14. title: "trashed cards",
  15. count: -> { Card.where(trash: true) },
  16. link_text: "empty trash",
  17. task: "empty_trash"
  18. }
  19. )
  20. 1 module ClassMethods
  21. 1 def empty_trash
  22. Card.delete_trashed_files
  23. Card.where(trash: true).in_batches.update_all(left_id: nil, right_id: nil)
  24. Card.where(trash: true).in_batches.delete_all
  25. Card::Action.delete_cardless
  26. Card::Change.delete_actionless
  27. Card::Act.delete_actionless
  28. Card::Reference.unmap_if_referee_missing
  29. Card::Reference.delete_if_referer_missing
  30. end
  31. # deletes any file not associated with a real card.
  32. 1 def delete_trashed_files
  33. dir = Cardio.paths["files"].existent.first
  34. # TODO: handle cloud files
  35. return unless dir
  36. trashed_card_ids = all_trashed_card_ids
  37. file_ids = all_file_ids
  38. file_ids.each do |file_id|
  39. next unless trashed_card_ids.member?(file_id)
  40. raise Card::Error, tr(:exception_almost_deleted) if Card.exists?(file_id)
  41. ::FileUtils.rm_rf "#{dir}/#{file_id}", secure: true
  42. end
  43. end
  44. 1 def all_file_ids
  45. dir = Card.paths["files"].existent.first
  46. Dir.entries(dir)[2..-1].map(&:to_i)
  47. end
  48. 1 def all_trashed_card_ids
  49. trashed_card_sql = %( select id from cards where trash is true )
  50. sql_results = Card.connection.select_all(trashed_card_sql)
  51. sql_results.map(&:values).flatten.map(&:to_i)
  52. end
  53. end
  54. 1 def delete args={}
  55. add_to_trash args do |delete_args|
  56. update delete_args
  57. end
  58. end
  59. 1 def delete! args={}
  60. 7 add_to_trash args do |delete_args|
  61. 7 update! delete_args
  62. end
  63. end
  64. 1 def add_to_trash args
  65. 7 return if new_card?
  66. 7 yield args.merge trash: true
  67. end
  68. 1 event :manage_trash, :prepare_to_store, on: :create do
  69. 134 pull_from_trash!
  70. 134 self.trash = false
  71. 134 true
  72. end
  73. 1 def pull_from_trash!
  74. 134 return unless (id = Card::Lexicon.id key) # name is already known
  75. return unless (trashed_card = Card.where(id: id).take)&.trash
  76. # confirm name is actually in trash
  77. db_attributes["id"] = trashed_card.db_attributes["id"]
  78. # id_in_database returns existing card id
  79. @from_trash = true
  80. @new_record = false
  81. end
  82. 1 def db_attributes
  83. send(:mutations_from_database).send :attributes
  84. end
  85. 1 event :validate_delete, :validate, on: :delete do
  86. 12 unless codename.blank?
  87. 6 errors.add :delete, tr(:error_system_card, name: name, codename: codename)
  88. end
  89. undeletable_all_rules_tags =
  90. 12 %w[default style layout create read update delete]
  91. # FIXME: HACK! should be configured in the rule
  92. 12 if junction? && left&.codename == :all &&
  93. undeletable_all_rules_tags.member?(right.codename.to_s)
  94. errors.add :delete, tr(:error_indestructible, name: name)
  95. end
  96. 12 errors.add :delete, tr(:error_user_edits, name: name) if account && has_edits?
  97. end
  98. 1 event :validate_delete_children, after: :validate_delete, on: :delete do
  99. 12 return if errors.any?
  100. 6 each_child do |child|
  101. 5 next unless child
  102. # prevents errors in cases where a child is deleted prior to this point
  103. # and thus is not returned by the fetch in #children
  104. 5 delete_as_subcard child
  105. # next if child.valid?
  106. # child.errors.each do |field, message|
  107. # errors.add field, "can't delete #{child.name}: #{message}"
  108. # end
  109. end
  110. end
  111. 1 def delete_as_subcard subcard
  112. 5 subcard.trash = true
  113. 5 add_subcard subcard
  114. end
  115. end;end;end;end;
  116. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/trash.rb ~~

card/tmpsets/set/mod003-core/all/type.rb

88.24% lines covered

34 relevant lines. 30 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Type)
  4. #
  5. 1 module Type;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/type.rb"; end
  8. 1 module ClassMethods
  9. 1 def default_type_id
  10. 72 @@default_type_id ||= Card[:all].fetch(:default, skip_modules: true).type_id
  11. end
  12. end
  13. 1 def type_card
  14. 11199 return if type_id.nil?
  15. 11199 Card.quick_fetch type_id.to_i
  16. end
  17. 1 def type_code
  18. 2474 Card::Codename[type_id.to_i]
  19. end
  20. 1 def type_name
  21. 10940 type_card.try :name
  22. end
  23. 1 alias_method :type, :type_name
  24. 1 def type_name_or_default
  25. 4 type_card.try(:name) || Card.quick_fetch(Card.default_type_id).name
  26. end
  27. 1 def type_cardname
  28. type_card.try :name
  29. end
  30. 1 def type= type_name
  31. self.type_id = Card.fetch_id type_name
  32. end
  33. 1 def type_id= card_or_id
  34. 5292 write_card_or_id :type_id, card_or_id
  35. end
  36. 1 def type_id_from_template
  37. 1303 return unless name && (t = template)
  38. 1303 reset_patterns # still necessary even with new template handling?
  39. 1303 self.type_id = t.type_id
  40. end
  41. 1 event :validate_type_change, :validate, on: :update, changed: :type_id do
  42. 1 if (c = dup) && c.action == :create && !c.valid?
  43. errors.add :type, tr(
  44. :error_cant_change_errors,
  45. name: name, type_id: type_id,
  46. error_messages: c.errors.full_messages
  47. )
  48. end
  49. end
  50. 1 event :validate_type, :validate, changed: :type_id, on: :save do
  51. 136 errors.add :type, tr(:error_no_such_type) unless type_name
  52. 136 if (rt = structure) && rt.assigns_type? && type_id != rt.type_id
  53. errors.add :type, tr(:error_hard_templated, name: name, type_name: rt.type_name)
  54. end
  55. end
  56. end;end;end;end;
  57. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/type.rb ~~

card/tmpsets/set/mod003-core/all/update_read_rules.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (UpdateReadRules)
  4. #
  5. 1 module UpdateReadRules;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/update_read_rules.rb"; end
  8. # FIXME: the following don't really belong here, but they have to come after
  9. # the reference stuff. we need to organize a bit!
  10. 1 event :update_rule_cache, :finalize, when: :is_rule? do
  11. 39 Card::Rule.clear_rule_cache
  12. end
  13. 1 event :expire_related, :finalize do
  14. 216 reset_patterns
  15. 230 structuree_names.each { |name| Director.expirees << name } if is_structure?
  16. end
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/update_read_rules.rb ~~

card/tmpsets/set/mod003-core/all/utils.rb

31.48% lines covered

54 relevant lines. 17 lines covered and 37 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Utils)
  4. #
  5. 1 module Utils;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/core/set/all/utils.rb"; end
  8. 1 module ClassMethods
  9. 1 def merge_list attribs, opts={}
  10. unmerged = []
  11. attribs.each do |row|
  12. if merge row["name"], row, opts
  13. Rails.logger.info "merged #{row['name']}"
  14. else
  15. unmerged.push row
  16. end
  17. end
  18. if unmerged.empty?
  19. Rails.logger.info "successfully merged all!"
  20. else
  21. unmerged_json = JSON.pretty_generate unmerged
  22. report_unmerged_json unmerged_json, opts[:output_file]
  23. end
  24. unmerged
  25. end
  26. 1 def report_unmerged_json unmerged_json, output_file
  27. if output_file
  28. ::File.open output_file, "w" do |f|
  29. f.write unmerged_json
  30. end
  31. else
  32. Rails.logger.info "failed to merge:\n\n#{unmerged_json}"
  33. end
  34. end
  35. 1 def merge name, attribs={}, opts={}
  36. # puts "merging #{name}"
  37. card = fetch name, new: {}
  38. return unless mergeable? card, opts[:pristine]
  39. resolve_file_attributes! attribs
  40. card.safe_update! attribs
  41. end
  42. 1 private
  43. 1 def resolve_file_attributes! attribs
  44. %i[image file].each do |attach|
  45. next unless attribs[attach] && attribs[attach].is_a?(String)
  46. attribs[attach] = ::File.open(attribs[attach])
  47. end
  48. end
  49. 1 def mergeable? card, pristine_only
  50. return true unless pristine_only
  51. !card.pristine?
  52. end
  53. end
  54. # separate name and other attributes
  55. 1 def safe_update! attribs
  56. separate_name_update! attribs.delete("name") unless new?
  57. update! attribs if attribs.present?
  58. end
  59. 1 def separate_name_update! new_name
  60. return if new_name.to_s == name.to_s
  61. update! name: new_name
  62. end
  63. # rubocop:disable Style/GlobalVars
  64. 1 def measure desc
  65. $times ||= {}
  66. res = nil
  67. t = Benchmark.measure do
  68. res = yield
  69. end
  70. $times[desc] = $times.key?(desc) ? t + $times[desc] : t
  71. puts "#{desc}: #{t}".red
  72. res
  73. end
  74. # rubocop:enable Style/GlobalVars
  75. 1 def mod_root modname
  76. if (spec = Gem::Specification.find_by_name "card-mod-#{modname}")
  77. spec.full_gem_path
  78. else
  79. "#{Cardio.gem_root}/mod/#{modname}"
  80. end
  81. end
  82. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  83. 1 delegate :measure, to: :card
  84. end
  85. end;end;end;end;
  86. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set/all/utils.rb ~~

card/tmpsets/set/mod004-card-mod-format/all/all_css.rb

54.17% lines covered

24 relevant lines. 13 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (AllCss)
  4. #
  5. 1 module AllCss;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/all_css.rb"; end
  8. 2 module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
  9. 1 def default_nest_view
  10. :raw
  11. end
  12. 1 def show view, args
  13. view ||= :content
  14. render! view, args
  15. end
  16. 1 view :titled do
  17. major_comment(%( Style Card: \\"#{card.name}\\" )) + _render_core
  18. end
  19. 1 view :content do
  20. _render_core
  21. end
  22. 1 view :unknown do
  23. major_comment "MISSING Style Card: #{card.name}"
  24. end
  25. 1 view :import do
  26. _render_core
  27. end
  28. 1 view :url, perms: :none do
  29. path mark: card.name, format: :css
  30. end
  31. 1 def major_comment comment, char="-"
  32. edge = %(/* #{char * (comment.length + 4)} */)
  33. main = %(/* #{char} #{comment} #{char} */)
  34. "#{edge}\n#{main}\n#{edge}\n\n"
  35. end
  36. end
  37. end;end;end;end;
  38. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/all_css.rb ~~

card/tmpsets/set/mod004-card-mod-format/all/all_csv.rb

38.89% lines covered

54 relevant lines. 21 lines covered and 33 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (AllCsv)
  4. #
  5. 1 module AllCsv;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/all_csv.rb"; end
  8. 1 require "csv"
  9. 2 module CsvFormat; module_parent.send :register_set_format, Card::Format::CsvFormat, self; extend Card::Set::AbstractFormat
  10. 1 def default_nest_view
  11. :core
  12. end
  13. 1 def default_item_view
  14. depth.zero? ? :csv_row : :name
  15. end
  16. 1 view :core do
  17. if (item_view_options[:view] == :name_with_fields) && focal?
  18. title_row("item name") + name_with_field_rows
  19. else
  20. super()
  21. end
  22. end
  23. 1 view :csv_row do
  24. array = _render_raw.scan(/\{\{[^\}]*\}\}/).map do |inc|
  25. process_content(inc).strip
  26. end
  27. CSV.generate_line(array).strip
  28. # strip is because search already joins with newlines
  29. end
  30. 1 view :unknown do
  31. ""
  32. end
  33. 1 view :name_with_fields do
  34. CSV.generate_line name_with_fields_row
  35. end
  36. 1 def name_with_fields_row
  37. nested_field_names.each_with_object([card.name]) do |field_name, row|
  38. row << nest(field_name)
  39. end
  40. end
  41. 1 def name_with_field_rows
  42. return [] unless row_card_names.present?
  43. row_card_names.map do |item_name|
  44. CSV.generate_line row_from_field_names(item_name, columns)
  45. end.join
  46. end
  47. 1 def row_card_names
  48. @row_cards ||= card.item_names
  49. end
  50. 1 def columns
  51. csv_structure_card.format.nested_field_names.map(&:tag)
  52. end
  53. 1 def csv_structure_card
  54. card.rule_card(:csv_structure) || Card.fetch(row_card_names.first)
  55. end
  56. 1 def row_from_field_names parent_name, field_names, view=:core
  57. field_names.each_with_object([parent_name]) do |field, row|
  58. row << nest([parent_name, field], view: view)
  59. end
  60. end
  61. 1 def title_row extra_titles=nil
  62. titles = column_titles extra_titles
  63. return "" unless titles.present?
  64. CSV.generate_line titles.map(&:upcase)
  65. end
  66. 1 def column_titles extra_titles=nil
  67. res = Array extra_titles
  68. card1 = Card.fetch card.item_names(limit: 1).first
  69. card1.nest_chunks.each do |chunk|
  70. res << column_title(chunk.options)
  71. end
  72. res.compact
  73. end
  74. 1 def column_title opts
  75. if opts[:title]
  76. opts[:title]
  77. elsif %w[name link].member? opts[:view]
  78. opts[:view]
  79. else
  80. opts[:nest_name].to_name.tag
  81. end
  82. end
  83. end
  84. end;end;end;end;
  85. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/all_csv.rb ~~

card/tmpsets/set/mod004-card-mod-format/all/all_js.rb

77.78% lines covered

9 relevant lines. 7 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (AllJs)
  4. #
  5. 1 module AllJs;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/all_js.rb"; end
  8. 2 module JsFormat; module_parent.send :register_set_format, Card::Format::JsFormat, self; extend Card::Set::AbstractFormat
  9. 1 def default_item_view
  10. :core
  11. end
  12. 1 view :source do
  13. path format: :js
  14. end
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/all_js.rb ~~

card/tmpsets/set/mod004-card-mod-format/all/base.rb

77.78% lines covered

72 relevant lines. 56 lines covered and 16 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Base)
  4. #
  5. 1 module Base;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/base.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 def show view, args
  10. 30 view ||= :core
  11. 30 render! view, args.merge(main_nest_options)
  12. end
  13. # NAME VIEWS
  14. 1 view :name, compact: true, perms: :none do
  15. 72 name_variant safe_name
  16. end
  17. 1 def safe_name
  18. 324 card&.name
  19. end
  20. 1 def name_variant name
  21. 1755 voo.variant ? name.to_name.vary(voo.variant) : name
  22. end
  23. 1 view(:key, compact: true, perms: :none) { card.key }
  24. 37 view(:linkname, compact: true, perms: :none) { card.name.url_key }
  25. 37 view(:url, compact: true, perms: :none) { card_url _render_linkname }
  26. 1 view :url_link, compact: true, perms: :none do
  27. link_to_resource card_url(_render_linkname)
  28. end
  29. 1 view :link, compact: true, perms: :none do
  30. 147 link_view
  31. end
  32. 1 view :nav_link, compact: true, perms: :none do
  33. link_view class: "nav-link"
  34. end
  35. 1 def link_view opts={}
  36. 371 opts[:known] = card.known?
  37. 371 specify_type_in_link! opts
  38. 371 link_to_card card.name, _render_title, opts
  39. end
  40. 1 def specify_type_in_link! opts
  41. 371 return if opts[:known] || !voo.type
  42. opts[:path] = { card: { type: voo.type } }
  43. end
  44. 1 view(:codename, compact: true) { card.codename.to_s }
  45. 1 view(:id, compact: true) { card.id }
  46. 1 view(:type, compact: true) { card.type_name }
  47. # DATE VIEWS
  48. 1 view(:created_at, compact: true) { date_view card.created_at }
  49. 1 view(:updated_at, compact: true) { date_view card.updated_at }
  50. 1 view(:acted_at, compact: true) { date_view card.acted_at }
  51. 1 def date_view date
  52. if voo.variant
  53. date.strftime voo.variant
  54. else
  55. time_ago_in_words date
  56. end
  57. end
  58. # CONTENT VIEWS
  59. 1 view :raw do
  60. 2385 structure_card&.content || _render_blank
  61. end
  62. 1 def structure_card
  63. 2385 return nil if voo.structure == true
  64. 2385 voo.structure ? Card[voo.structure] : card
  65. end
  66. 1 view :core, compact: true do
  67. 1624 process_content _render_raw
  68. end
  69. 1 view :content do
  70. _render_core
  71. end
  72. 1 view :open_content do
  73. _render_core
  74. end
  75. 1 view :one_line_content, compact: true do
  76. with_nest_mode :compact do
  77. Card::Content.smart_truncate _render_core
  78. end
  79. end
  80. 1 view :labeled_content, unknown: :mini_unknown do
  81. 6 render_core
  82. end
  83. 1 view :titled_content, unknown: :blank do
  84. 73 render_core
  85. end
  86. 1 view :blank, compact: true, perms: :none do
  87. 527 ""
  88. end
  89. # note: content and open_content may look like they should be aliased to
  90. # core, but it's important that they render core explicitly so that core view
  91. # overrides work. the titled and labeled views below, however, are not
  92. # intended for frequent override, so this shortcut is fine.
  93. # NAME + CONTENT VIEWS
  94. 1 view :titled do
  95. "#{card.name}\n\n#{_render_core}"
  96. end
  97. 1 view :open, :titled
  98. 1 view :labeled do
  99. "#{card.name}: #{_render_labeled_content}"
  100. end
  101. 1 view :closed, :labeled
  102. # SPECIAL VIEWS
  103. 1 view :array, cache: :never do
  104. card.item_cards(limit: 0).map do |item_card|
  105. subformat(item_card)._render_core
  106. end.inspect
  107. end
  108. # DEPRECATED
  109. 1 view :naked do
  110. Rails.logger.info "DEPRECATED: naked view (used with #{card.name} card)"
  111. render_core
  112. end
  113. end
  114. end;end;end;end;
  115. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/base.rb ~~

card/tmpsets/set/mod004-card-mod-format/all/file.rb

77.78% lines covered

9 relevant lines. 7 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (File)
  4. #
  5. 1 module File;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/file.rb"; end
  8. 2 module FileFormat; module_parent.send :register_set_format, Card::Format::FileFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :core do
  10. "File rendering of this card not yet supported"
  11. end
  12. 1 view :style do
  13. nil
  14. end
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/file.rb ~~

card/tmpsets/set/mod004-card-mod-format/all/head.rb

93.42% lines covered

76 relevant lines. 71 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Head)
  4. #
  5. 1 module Head;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/head.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 view :page_title, unknown: true, perms: :none do
  10. 224 title_parts = [Card::Rule.global_setting(:title)]
  11. 224 title_parts.unshift safe_name if card.name.present?
  12. 224 title_parts.join " - "
  13. end
  14. end
  15. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  16. # add tuples containing a
  17. # - the codename of a card with javascript config (usually in json format)
  18. # - the name of a javascript method that handles the config
  19. 1 basket :mod_js_config
  20. 1 view :head, unknown: true, perms: :none do
  21. 2688 views_in_head.map { |viewname| render viewname }.flatten.compact.join "\n"
  22. end
  23. 1 def views_in_head
  24. 224 %i[meta_tags page_title_tag favicon_tag head_stylesheet
  25. decko_script_variables head_javascript html5shiv_tag
  26. script_config_and_initiation
  27. universal_edit_button rss_links]
  28. end
  29. # FIXME: tags not working with `template: :haml`
  30. 1 view :meta_tags, unknown: true, perms: :none do
  31. 224 haml :meta_tags
  32. end
  33. 1 view :html5shiv_tag, unknown: true, perms: :none do
  34. 224 nest :script_html5shiv_printshiv, view: :script_tag
  35. end
  36. 1 view :page_title_tag, unknown: true, perms: :none do
  37. 448 content_tag(:title) { render :page_title }
  38. end
  39. 1 view :favicon_tag, unknown: true, perms: :none do
  40. 224 nest :favicon, view: :link_tag
  41. end
  42. 1 view :universal_edit_button, unknown: true, denial: :blank, perms: :update do
  43. 213 return if card.new?
  44. 160 tag "link", rel: "alternate", type: "application/x-wiki",
  45. title: "Edit this page!", href: path(view: :edit)
  46. end
  47. # these should render a view of the rule card
  48. # it would then be safe to cache if combined with param handling
  49. # (but note that machine clearing would need to reset card cache...)
  50. 1 view :head_stylesheet, unknown: true, cache: :never, perms: :none do
  51. 224 return unless (href = head_stylesheet_path)
  52. 224 tag "link", href: href, media: "all", rel: "stylesheet", type: "text/css"
  53. end
  54. 1 view :head_javascript, unknown: true, cache: :never, perms: :none do
  55. 224 Array.wrap(head_javascript_paths).map do |path|
  56. 224 javascript_include_tag path
  57. end
  58. end
  59. 1 view :decko_script_variables, unknown: true, cache: :never, perms: :none do
  60. 224 string = ""
  61. 224 decko_script_variables.each do |k, v|
  62. 896 string += "#{k}=#{script_variable_to_js v};\n"
  63. end
  64. 448 javascript_tag { string }
  65. end
  66. 1 def decko_script_variables
  67. {
  68. 224 "window.decko": { rootUrl: card_url("") },
  69. "decko.doubleClick": Card.config.double_click,
  70. "decko.cssPath": head_stylesheet_path,
  71. 224 "decko.currentUserId": (Auth.current_id if Auth.signed_in?)
  72. }
  73. end
  74. 1 def script_variable_to_js value
  75. 1120 if value.is_a? Hash
  76. 224 string = "{"
  77. 448 value.each { |k, v| string += "#{k}:#{script_variable_to_js v}" }
  78. 224 string + "}"
  79. else
  80. 896 "'#{value}'"
  81. end
  82. end
  83. 1 def param_or_rule_card setting
  84. 672 if params[setting]
  85. Card[params[setting]]
  86. else
  87. 672 root.card.rule_card setting
  88. end
  89. end
  90. 1 def debug_or_machine_path setting, &block
  91. 672 return unless (asset_card = param_or_rule_card setting)
  92. 672 debug_path(setting, asset_card, &block) || asset_card.machine_output_url
  93. end
  94. 1 def debug_path setting, asset_card
  95. 672 return unless params[:debug] == setting.to_s
  96. yield asset_card
  97. end
  98. 1 def head_stylesheet_path
  99. 448 debug_or_machine_path :style do |style_card|
  100. path mark: style_card.name, item: :import, format: :css
  101. end
  102. end
  103. 1 def head_javascript_paths
  104. 224 debug_or_machine_path :script do |script_card|
  105. script_card.item_cards.map do |script|
  106. script.format(:js).render :source
  107. end
  108. end
  109. end
  110. 1 view :script_config_and_initiation, unknown: true, perms: :none do
  111. 224 javascript_tag do
  112. 224 (mod_js_configs << trigger_slot_ready).join "\n\n"
  113. end
  114. end
  115. 1 def mod_js_configs
  116. 224 mod_js_config.map do |codename, js_decko_function|
  117. 896 config_json = escape_javascript Card::Rule.global_setting(codename)
  118. 896 "decko.#{js_decko_function}('#{config_json}')"
  119. end
  120. end
  121. 1 def trigger_slot_ready
  122. 224 "$('document').ready(function() { $('.card-slot').trigger('slotReady'); })"
  123. end
  124. # TODO: move to rss mod
  125. 1 view :rss_links, unknown: true, perms: :none do
  126. 224 render :rss_link_tag if rss_link?
  127. end
  128. 1 def rss_link?
  129. 224 Card.config.rss_enabled && respond_to?(:rss_link_tag)
  130. end
  131. end
  132. end;end;end;end;
  133. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/head.rb ~~

card/tmpsets/set/mod004-card-mod-format/all/json.rb

53.42% lines covered

73 relevant lines. 39 lines covered and 34 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Json)
  4. #
  5. 1 module Json;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/json.rb"; end
  8. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  9. # because card.item_cards returns "[[#{self}]]"
  10. 1 def item_cards
  11. nested_cards
  12. end
  13. 1 def default_nest_view
  14. :atom
  15. end
  16. 1 def default_item_view
  17. params[:item] || :name
  18. end
  19. 1 def max_depth
  20. params[:max_depth].present? ? params[:max_depth].to_i : 1
  21. end
  22. # TODO: support layouts in json
  23. # eg layout=stamp gives you the metadata currently in "page" view
  24. # and layout=none gives you ONLY the requested view (default atom)
  25. 1 def show view, args
  26. 11 view ||= :molecule
  27. 11 raw = render! view, args
  28. 11 raw.is_a?(String) ? raw : stringify(raw)
  29. end
  30. 1 def stringify raw
  31. 11 method = params[:compress] ? :generate : :pretty_generate
  32. 11 JSON.send method, raw
  33. end
  34. 1 view :status, unknown: true, perms: :none do
  35. 8 status = card.state
  36. 8 hash = { key: card.key,
  37. url_key: card.name.url_key,
  38. status: status }
  39. 8 hash[:id] = card.id if status == :real
  40. 8 hash
  41. end
  42. # NOCACHE because of timestamp
  43. 1 view :page, cache: :never do
  44. { url: request_url,
  45. timestamp: Time.now.to_s,
  46. card: _render_atom }
  47. end
  48. 1 def request_url
  49. req = controller.request
  50. req ? req.original_url : path
  51. end
  52. 1 view :core, unknown: true do
  53. card.known? ? render_content : nil
  54. end
  55. 1 view :content do
  56. card.content
  57. end
  58. 1 view :nucleus do
  59. nucleus
  60. end
  61. # TODO: add simple values for fields
  62. 1 view :atom, unknown: true do
  63. atom
  64. end
  65. 1 view :molecule do
  66. molecule
  67. end
  68. # NOCACHE because sometimes item_cards is dynamic.
  69. # could be safely cached for non-dynamic lists
  70. 1 view :items, cache: :never do
  71. listing item_cards, view: :atom
  72. end
  73. 1 view :links do
  74. card.link_chunks.map do |chunk|
  75. if chunk.referee_name
  76. path mark: chunk.referee_name, format: :json
  77. else
  78. link_to_resource chunk.link_target
  79. end
  80. end
  81. end
  82. 1 view :ancestors do
  83. card.name.ancestors.map do |name|
  84. nest name, view: :nucleus
  85. end
  86. end
  87. # minimum needed to re-fetch card
  88. 1 view :cast do
  89. card.cast
  90. end
  91. ## DEPRECATED
  92. 1 view :marks do
  93. {
  94. id: card.id,
  95. name: card.name,
  96. key: card.key,
  97. url: path
  98. }
  99. end
  100. 1 view :essentials do
  101. if voo.show? :marks
  102. render_marks.merge(essentials)
  103. else
  104. essentials
  105. end
  106. end
  107. 1 def essentials
  108. return {} if card.structure
  109. { content: card.db_content }
  110. end
  111. # NOTE: moving these to methods prevents potential caching problems, because other
  112. # views manipulate their hashes.
  113. #
  114. 1 def nucleus
  115. h = { id: card.id,
  116. name: card.name,
  117. type: card.type_name,
  118. url: path(format: :json) }
  119. h[:codename] = card.codename if card.codename
  120. h
  121. end
  122. 1 def atom
  123. h = nucleus
  124. h[:content] = render_content if card.known? && !card.structure
  125. h
  126. end
  127. 1 def molecule
  128. atom.merge items: _render_items,
  129. links: _render_links,
  130. ancestors: _render_ancestors,
  131. html_url: path,
  132. type: nest(card.type_card, view: :nucleus)
  133. end
  134. end
  135. # TODO: perhaps this should be in a general "data" module.
  136. 1 def cast
  137. real? ? { id: id } : { name: name, type_id: type_id, content: db_content }
  138. end
  139. end;end;end;end;
  140. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/json.rb ~~

card/tmpsets/set/mod004-card-mod-format/all/rss.rb

45.83% lines covered

48 relevant lines. 22 lines covered and 26 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Rss)
  4. #
  5. 1 module Rss;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/rss.rb"; end
  8. 2 module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
  9. 1 attr_accessor :xml
  10. 1 def initialize card, args
  11. super
  12. @xml = @parent ? @parent.xml : ::Builder::XmlMarkup.new
  13. end
  14. 1 def show view, args
  15. view ||= :feed
  16. render! view, args
  17. end
  18. # FIXME: integrate this with common XML features when it is added
  19. 1 view :feed, cache: :never do
  20. return "RSS feeds disabled" unless Cardio.config.rss_enabled
  21. begin
  22. @xml.instruct! :xml, version: "1.0", standalone: "yes"
  23. @xml.rss version: "2.0",
  24. "xmlns:content" => "http://purl.org/rss/1.0/modules/content/" do
  25. @xml.channel do
  26. @xml.title render_feed_title
  27. @xml.description render_feed_description
  28. @xml.link render_url
  29. render_feed_body
  30. end
  31. end
  32. rescue => e
  33. @xml.error "\n\nERROR rendering RSS: #{e.inspect}\n\n #{e.backtrace}"
  34. end
  35. end
  36. 1 def raw_feed_items
  37. [card]
  38. end
  39. 1 view :feed_body, cache: :never do
  40. raw_feed_items.each do |item|
  41. @xml.item do
  42. subformat(item).render! :feed_item
  43. end
  44. end
  45. end
  46. 1 view :feed_title do
  47. Card::Rule.global_setting(:title) + " : " + card.name.gsub(/^\*/, "")
  48. end
  49. 1 view :feed_item do
  50. @xml.title card.name
  51. add_name_context
  52. @xml.description render_feed_item_description
  53. @xml.pubDate pub_date
  54. @xml.link render_url
  55. @xml.guid render_url
  56. end
  57. 1 def pub_date
  58. (card.updated_at || Time.zone.now).to_s(:rfc822)
  59. # updated_at fails on virtual
  60. # cards, because not all to_s's take args (just actual dates)
  61. end
  62. 1 view :feed_item_description do
  63. render_open_content
  64. end
  65. 1 view(:feed_description) { "" }
  66. 1 view(:comment_box) { "" }
  67. 1 view(:menu) { "" }
  68. 1 view :open, :titled, mod: All::Base::Format
  69. 1 view :content, :core, mod: All::Base::Format
  70. 1 view :open_content, :core, mod: All::Base::Format
  71. 1 view :closed, :link, mod: All::Base::Format
  72. end
  73. end;end;end;end;
  74. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/rss.rb ~~

card/tmpsets/set/mod004-card-mod-format/all/text.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Text)
  4. #
  5. 1 module Text;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/all/text.rb"; end
  8. 2 module TextFormat; module_parent.send :register_set_format, Card::Format::TextFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :core do
  10. 48 HTMLEntities.new.decode strip_tags(super()).to_s
  11. # need this string method to get out of html_safe mode
  12. end
  13. end
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/all/text.rb ~~

card/tmpsets/set/mod004-card-mod-format/self/head.rb

91.67% lines covered

12 relevant lines. 11 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Head"
  4. #
  5. 1 module Head;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/self/head.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # when *head is rendered in the main body of a page, we escape the HTML
  10. # otherwise (most typically in the head tag, of course), we render the
  11. # HTML unescaped
  12. 1 view :core, cache: :never do
  13. 224 escape_in_main do
  14. 224 nest root.card, view: :head
  15. # note that the head tag for each card is different
  16. # (different title, different style rules, etc)
  17. # so we don't cache the core of *head, but we _do_ cache some
  18. # views within each head (see all/head.rb)
  19. end
  20. end
  21. 1 view :input do
  22. "Content can't be edited."
  23. end
  24. 1 def escape_in_main
  25. 224 main? ? (h yield) : yield
  26. end
  27. end
  28. end;end;end;end;
  29. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/self/head.rb ~~

card/tmpsets/set/mod004-card-mod-format/type/html.rb

83.33% lines covered

18 relevant lines. 15 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Html" cards
  4. #
  5. 1 module Html;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/type/html.rb"; end
  8. 1 def clean_html?
  9. 3 false
  10. end
  11. 1 def diff_args
  12. { diff_format: :raw }
  13. end
  14. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  15. 1 view :one_line_content do
  16. raw_one_line_content
  17. end
  18. 1 def chunk_list
  19. 1158 :references
  20. end
  21. end
  22. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  23. 1 def input_type
  24. 3 :ace_editor
  25. end
  26. 1 view :one_line_content, wrap: {} do
  27. raw_one_line_content
  28. end
  29. end
  30. end;end;end;end;
  31. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/type/html.rb ~~

card/tmpsets/set/mod004-card-mod-format/type/json.rb

70.83% lines covered

24 relevant lines. 17 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Json" cards
  4. #
  5. # include_set Abstract::Pointer
  6. 1 module Json;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/type/json.rb"; end
  9. 1 event :validate_json, :validate, on: :save, changed: :content do
  10. 20 check_json_syntax if content.present?
  11. end
  12. 1 def check_json_syntax
  13. 20 parse_content
  14. rescue JSON::ParserError => e
  15. errors.add tr(:invalid_json), e.message.sub(/^\d+: /, "").to_s
  16. end
  17. 1 def parse_content
  18. 20 JSON.parse content
  19. end
  20. 1 def item_names _args={}
  21. parse_content.keys.map(&:to_name)
  22. end
  23. 1 def item_values
  24. parse_content.values
  25. end
  26. 1 def item_value name
  27. parse_content[name]
  28. end
  29. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  30. 1 view :core do
  31. process_content ::CodeRay.scan(_render_raw, :json).div
  32. end
  33. 1 def input_type
  34. :ace_editor
  35. end
  36. 1 def ace_mode
  37. :json
  38. end
  39. end
  40. end;end;end;end;
  41. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/type/json.rb ~~

card/tmpsets/set/mod004-card-mod-format/type/plain_text.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "PlainText" cards
  4. #
  5. 1 module PlainText;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-format/set/type/plain_text.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def input_type
  10. 1 :text_area
  11. end
  12. 1 view :core do
  13. 1 process_content CGI.escapeHTML(_render_raw)
  14. end
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-format/set/type/plain_text.rb ~~

card/tmpsets/set/mod005-pointer/abstract/00_paging_params.rb

95.0% lines covered

20 relevant lines. 19 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (PagingParams)
  4. #
  5. 1 module PagingParams;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/00_paging_params.rb"; end
  8. 1 MAX_ANONYMOUS_SEARCH_PARAM = 1000
  9. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  10. 1 def limit_param
  11. 59 @limit ||= contextual_param(:limit) || default_limit
  12. end
  13. 1 def offset_param
  14. 59 @offset ||= contextual_param(:offset) || 0
  15. end
  16. 1 def contextual_param param
  17. 54 env_search_param(param) || voo_search_param(param)
  18. end
  19. 1 def env_search_param param
  20. 54 return unless focal? && Env.params[param].present?
  21. 2 legal_search_param! param, Env.params[param].to_i
  22. end
  23. 1 def legal_search_param! param, val
  24. 2 return val if Card::Auth.signed_in? || val <= MAX_ANONYMOUS_SEARCH_PARAM
  25. raise Card::Error::PermissionDenied, "#{param} parameter exceeds maximum"
  26. end
  27. 1 def voo_search_param param
  28. 52 voo&.cql&.dig param
  29. end
  30. end
  31. end;end;end;end;
  32. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/00_paging_params.rb ~~

card/tmpsets/set/mod005-pointer/abstract/01_paging.rb

86.49% lines covered

74 relevant lines. 64 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Paging)
  4. #
  5. 1 module Paging;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/01_paging.rb"; end
  8. 1 include_set Abstract::PagingParams
  9. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  10. 1 def limit
  11. 48 limit_param
  12. end
  13. 1 def offset
  14. 48 offset_param
  15. end
  16. 1 def search_with_params
  17. 16 card.item_names
  18. end
  19. 1 def count_with_params
  20. card.item_names.count
  21. end
  22. 1 def total_pages
  23. 3 return 1 if limit.zero?
  24. 3 ((count_with_params - 1) / limit).to_i
  25. end
  26. 1 def current_page
  27. 3 (offset / limit).to_i
  28. end
  29. # for override
  30. 1 def extra_paging_path_args
  31. 16 {}
  32. end
  33. end
  34. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  35. 1 PAGE_LI_CLASS = { ellipses: "disabled", current: "active" }.freeze
  36. 1 def with_paging path_args={}
  37. 20 with_paging_path_args path_args do
  38. 20 output [yield(@paging_path_args), _render_paging]
  39. end
  40. end
  41. 1 view :paging, cache: :never do
  42. 20 return "" unless paging_needed?
  43. 3 <<-HTML
  44. <nav>
  45. <ul class="pagination paging">
  46. #{paging_links.join}
  47. </ul>
  48. </nav>
  49. HTML
  50. end
  51. 1 def paging_links
  52. 3 PagingLinks.new(total_pages, current_page)
  53. .build do |text, page, status, options|
  54. 19 page_link_li text, page, status, options
  55. end
  56. end
  57. # First page is 0 (not 1)
  58. 1 def page_link_li text, page, status, options={}
  59. 19 wrap_with :li, class: page_link_li_class(status) do
  60. 19 page_link text, page, options
  61. end
  62. end
  63. 1 def page_link_li_class status
  64. 19 ["page-item", PAGE_LI_CLASS[status]].compact.join " "
  65. end
  66. 1 def page_link text, page, options
  67. 19 return content_tag(:div, text.html_safe, class: "page-link") unless page
  68. 15 options.merge! class: "card-paging-link slotter page-link",
  69. remote: true,
  70. path: page_link_path_args(page)
  71. 15 link_to raw(text), options
  72. end
  73. 1 def with_paging_path_args args
  74. 20 tmp = @paging_path_args
  75. 20 @paging_path_args = paging_path_args args
  76. 20 yield
  77. ensure
  78. 20 @paging_path_args = tmp
  79. end
  80. 1 def paging_path_args local_args={}
  81. 35 @paging_path_args ||= {}
  82. 35 @paging_path_args.reverse_merge!(limit: limit, offset: offset)
  83. 35 @paging_path_args.merge! extra_paging_path_args
  84. 35 @paging_path_args.merge local_args
  85. end
  86. 1 def page_link_path_args page
  87. 15 paging_path_args.merge offset: page * limit
  88. end
  89. 1 def paging_needed?
  90. 20 return false if limit < 1
  91. 20 return false if fewer_results_than_limit? # avoid extra count search
  92. # count search result instead
  93. 3 limit < count_with_params
  94. end
  95. # clear we don't need paging even before running count query
  96. 1 def fewer_results_than_limit?
  97. 20 return false unless offset.zero?
  98. 19 limit > offset + search_with_params.length
  99. end
  100. end
  101. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  102. 1 def page_link_path_args page
  103. {
  104. limit: limit,
  105. offset: page * limit,
  106. item: default_item_view, # hack. need standard voo handling
  107. format: :json
  108. }.merge extra_paging_path_args
  109. end
  110. 1 view :paging_urls, cache: :never do
  111. return {} unless total_pages > 1
  112. { paging: paging_urls_hash }
  113. end
  114. 1 def paging_urls_hash
  115. hash = {}
  116. PagingLinks.new(total_pages, current_page)
  117. .build do |_text, page, status, _options|
  118. add_paging_url hash, page, status
  119. end
  120. hash
  121. end
  122. 1 def add_paging_url hash, page, status
  123. return unless page && status.in?(%i[next previous])
  124. hash[status] = path page_link_path_args(page)
  125. end
  126. end
  127. end;end;end;end;
  128. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/01_paging.rb ~~

card/tmpsets/set/mod005-pointer/abstract/01_paging/paging_links.rb

100.0% lines covered

43 relevant lines. 43 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Paging)
  4. #
  5. #! no set module
  6. 1 module Paging;
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/01_paging/paging_links.rb"; end
  8. # render paging links
  9. 1 class PagingLinks
  10. 1 def initialize total_pages, current_page
  11. 3 @total = total_pages
  12. 3 @current = current_page
  13. end
  14. # @param window [integer] number of page links shown left and right
  15. # of the current page
  16. # @example: current page = 5, window = 2
  17. # |<<|1|...|3|4|[5]|6|7|...|10|>>|
  18. # @yield [text, page, status, options] block to build single paging link
  19. # @yieldparam status [Symbol] :active (for current page) or :disabled
  20. # @yieldparam page [Integer] page number, first page is 0
  21. # @return [Array<String>]
  22. 1 def build window=2, &block
  23. 3 @render_item = block
  24. 3 links window
  25. end
  26. 1 private
  27. 1 def links window
  28. 3 @window_start = [@current - window, 0].max
  29. 3 @window_end = [@current + window, @total].min
  30. 3 left_part + window_part + right_part
  31. end
  32. # the links around the current page
  33. 1 def window_part
  34. 3 (@window_start..@window_end).map do |page|
  35. 9 direct_page_link page
  36. end.compact
  37. end
  38. 1 def left_part
  39. [
  40. 3 previous_page_link,
  41. 3 (direct_page_link 0 if @window_start > 0),
  42. 3 (ellipse if @window_start > 1)
  43. ].compact
  44. end
  45. 1 def right_part
  46. [
  47. 3 (ellipse if @total > @window_end + 1),
  48. 3 (direct_page_link @total if @total > @window_end),
  49. next_page_link
  50. ].compact
  51. end
  52. 1 def previous_page_link
  53. 3 paging_item '<span aria-hidden="true">&laquo;</span>', previous_page,
  54. "aria-label" => "Previous", status: :previous
  55. end
  56. 1 def next_page_link
  57. 3 paging_item '<span aria-hidden="true">&raquo;</span>', next_page,
  58. "aria-label" => "Next", status: :next
  59. end
  60. 1 def direct_page_link page
  61. 11 return unless page >= 0 && page <= @total
  62. 11 paging_item page + 1, page
  63. end
  64. 1 def ellipse
  65. 2 paging_item "<span>...</span>", nil, status: :ellipses
  66. end
  67. 1 def paging_item text, page, options={}
  68. status =
  69. 19 if page == @current
  70. 3 :current
  71. else
  72. 16 options.delete :status
  73. end
  74. 19 @render_item.call text, page, status, options
  75. end
  76. 1 def previous_page
  77. 3 @current > 0 ? @current - 1 : false
  78. end
  79. 1 def next_page
  80. 3 @current < @total ? @current + 1 : false
  81. end
  82. end
  83. end;end;end;end;
  84. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/01_paging/paging_links.rb ~~

card/tmpsets/set/mod005-pointer/abstract/02_items.rb

75.86% lines covered

87 relevant lines. 66 lines covered and 21 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Items)
  4. #
  5. # ~~~~~~~~~~~~ READING ITEMS ~~~~~~~~~~~~
  6. 1 module Items;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_items.rb"; end
  9. # While each of the three main methods for returning lists of items can handle arguments,
  10. # they are most commonly used without them.
  11. # @return [Array] list of Card::Name objects
  12. # @param args [Hash]
  13. # @option args [String] :content override card content
  14. # @option args [String, Card::Name, Symbol] :context name in whose context relative items
  15. # will be interpreted. For example. +A in context of B is interpreted as B+A
  16. # context defaults to pointer card's name. If value is `:raw`, then name is not
  17. # contextualized
  18. # @option args [String, Integer] :limit max number of cards to return
  19. # @option args [String, Integer] :offset begin after the offset-th item
  20. 1 def item_names args={}
  21. 1774 context = args[:context]
  22. 1774 item_strings(args).map do |item|
  23. 4681 clean_item_name item, context
  24. end.compact
  25. end
  26. 1 def first_name args={}
  27. item_names(args).first
  28. end
  29. 1 def first_card args={}
  30. return unless (name = first_name)
  31. fetch_item_card name, args
  32. end
  33. 1 def first_code
  34. first_card&.codename
  35. end
  36. # @return [Array] list of integers (card ids of items)
  37. # @param args [Hash] see #item_names
  38. 1 def item_ids args={}
  39. 352 item_names(args).map { |name| Card.fetch_id name }.compact
  40. end
  41. # @return [Array] list of Card objects
  42. # @param args [Hash] see #item_names for additional options
  43. # @option args [String] :complete keyword to use in searching among items
  44. # @option args [True/False] :known_only if true, return only known cards
  45. # @option args [String] :type name of type to be used for unknown cards
  46. 1 def item_cards args={}
  47. 321 return item_cards_search(args) if args[:complete]
  48. 321 return known_item_cards(args) if args[:known_only]
  49. 321 all_item_cards args
  50. end
  51. # #item_name, #item_id, and #item_card each return a single item, rather than an array.
  52. 1 %i[name id card].each do |obj|
  53. 3 define_method "item_#{obj}" do |args={}|
  54. 259 send("item_#{obj}s", args.merge(limit: 1)).first
  55. end
  56. end
  57. # for override, eg by json
  58. 1 def item_value item_name
  59. item_name
  60. end
  61. # ~~~~~~~~~~~~ ALTERING ITEMS ~~~~~~~~~~~~
  62. # set card content based on array and save card
  63. # @param array [Array] list of strings/names (Cardish)
  64. 1 def items= array
  65. items_to_content array
  66. save!
  67. end
  68. # append item to list (does not save)
  69. # @param cardish [Cardish]
  70. 1 def << cardish
  71. add_item cardish
  72. end
  73. # append item to list (does not save)
  74. # @param cardish [String, Card::Name] item name
  75. # @param allow_duplicates [True/False] permit duplicate items (default is False)
  76. 1 def add_item cardish, allow_duplicates=false
  77. return if !allow_duplicates && include_item?(cardish)
  78. items = item_strings << cardish
  79. items_to_content items
  80. end
  81. # append item to list and save card
  82. # @param name [String, Card::Name] item name
  83. 1 def add_item! name
  84. add_item(name) && save!
  85. end
  86. # remove item from list
  87. # @param cardish [String, Card::Name] item to drop
  88. 1 def drop_item cardish
  89. drop_item_name = Card::Name[cardish]
  90. items_to_content(item_names.reject { |item_name| item_name == drop_item_name })
  91. end
  92. # remove item from list and save card
  93. # @param cardish [String, Card::Name] item to drop
  94. 1 def drop_item! cardish
  95. drop_item cardish
  96. save!
  97. end
  98. # insert item into list at specified location
  99. # @param index [Integer] Array index in which to insert item (0 is first)
  100. # @param name [String, Card::Name] item name
  101. 1 def insert_item index, name
  102. 37 new_names = item_names
  103. 37 new_names.delete name
  104. 37 new_names.insert index, name
  105. 37 items_to_content new_names
  106. end
  107. # insert item into list at specified location and save
  108. # @param index [Integer] Array index in which to insert item (0 is first)
  109. # @param name [String, Card::Name] item name
  110. 1 def insert_item! index, name
  111. insert_item index, name
  112. save!
  113. end
  114. # ~~~~~~~~~~~~ READING ITEM HELPERS ~~~~~~~~~~~~
  115. # Warning: the following methods, while available for use, may be subject to change
  116. # #item_cards helpers
  117. 1 def item_cards_search query
  118. Card::Query.run query.reverse_merge(referred_to_by: name, limit: 0)
  119. end
  120. 1 def known_item_cards args={}
  121. item_names(args).map { |name| Card.fetch name }.compact
  122. end
  123. 1 def all_item_cards args={}
  124. 321 names = args[:item_names] || item_names(args)
  125. 647 names.map { |name| fetch_item_card name, args }
  126. end
  127. # TODO: support type_code and type_id. (currently type)
  128. # uses name, because its most common use is from CQL
  129. 1 def item_type
  130. 345 opt = options_rule_card
  131. # FIXME: need better recursion prevention
  132. 345 return if !opt || opt == self
  133. 328 opt.item_type
  134. end
  135. 1 def item_strings args={}
  136. 1784 items = raw_item_strings(args[:content] || content)
  137. 1784 return items unless args.present?
  138. 381 filtered_items items, args.slice(:limit, :offset)
  139. end
  140. 1 def raw_item_strings content
  141. 6484 content.to_s.split(/\n+/).map { |i| strip_item i }
  142. end
  143. 1 private
  144. 1 def filtered_items items, limit: 0, offset: 0
  145. 381 limit = limit.to_i
  146. 381 offset = offset.to_i
  147. 381 return items unless limit.positive? || offset.positive?
  148. 271 items[offset, (limit.zero? ? items.size : limit)] || []
  149. end
  150. 1 def fetch_item_card name, args={}
  151. 326 Card.fetch name, new: new_unknown_item_args(args)
  152. end
  153. 1 def new_unknown_item_args args
  154. 326 itype = args[:type] || item_type
  155. 326 itype ? { type: itype } : {}
  156. end
  157. 1 def clean_item_name item, context
  158. 4681 item = item.to_name
  159. 4681 return item if context == :raw
  160. 4681 context ||= context_card.name
  161. 4681 item.absolute_name context
  162. rescue Card::Error::NotFound
  163. # eg for invalid ids or codenames
  164. # "Invalid Item: #{item}".to_name
  165. nil
  166. end
  167. 1 def strip_item item
  168. 4700 item.gsub(/\[\[|\]\]/, "").strip
  169. end
  170. end;end;end;end;
  171. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_items.rb ~~

card/tmpsets/set/mod005-pointer/abstract/02_pointer.rb

90.0% lines covered

10 relevant lines. 9 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Pointer)
  4. #
  5. 1 module Pointer;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer.rb"; end
  8. 1 include_set Abstract::Paging
  9. 1 include_set Abstract::Items
  10. 1 def diff_args
  11. { diff_format: :pointer }
  12. end
  13. 1 def count
  14. 5 item_strings.size
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer.rb ~~

card/tmpsets/set/mod005-pointer/abstract/02_pointer/events.rb

57.89% lines covered

38 relevant lines. 22 lines covered and 16 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Pointer;
  3. # Set: Abstract (Pointer, Events)
  4. #
  5. 1 module Events;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/events.rb"; end
  8. 1 event :add_and_drop_items, :prepare_to_validate, on: :save do
  9. 70 adds = Env.params["add_item"]
  10. 70 drops = Env.params["drop_item"]
  11. 70 Array.wrap(adds).each { |i| add_item i } if adds
  12. 70 Array.wrap(drops).each { |i| drop_item i } if drops
  13. end
  14. 1 event :insert_item_event, :prepare_to_validate, on: :save, when: :item_to_insert do
  15. index = Env.params["item_index"] || 0
  16. insert_item index.to_i, item_to_insert
  17. end
  18. 1 def item_to_insert
  19. 70 Env.params["insert_item"]
  20. end
  21. # If a card's type and content are updated in the same action, the new module
  22. # will override the old module's events and functions. But this event is only
  23. # on pointers -- other type cards do not have this event,
  24. # Therefore if something is changed from a pointer and its content is changed
  25. # in the same action, this event will be run and will treat the content like
  26. # it' still pointer content. The "when" clause helps with that (but is a hack)
  27. 1 event :standardize_items, :prepare_to_validate,
  28. on: :save, changed: :content, when: :still_pointer? do
  29. 5 items_to_content item_strings
  30. end
  31. 1 def still_pointer?
  32. 59 type_id == PointerID
  33. # Card.new(type_id: type_id).is_a? Abstract::Pointer
  34. end
  35. 1 def changed_item_names
  36. dropped_item_names + added_item_names
  37. end
  38. 1 def dropped_item_names
  39. return item_names if trash
  40. return [] unless (old_content = db_content_before_act)
  41. old_items = item_names content: old_content
  42. old_items - item_names
  43. end
  44. 1 def added_item_names
  45. return [] if trash
  46. return item_names unless (old_content = db_content_before_act)
  47. old_items = item_names content: old_content
  48. item_names - old_items
  49. end
  50. # TODO: refactor. many of the above could be written more elegantly with improved
  51. # handling of :content in item_names. If content is nil here, we would expect an
  52. # empty set of cards, but in fact we get items based on self.content.
  53. 1 def changed_item_cards
  54. dropped_item_cards + added_item_cards
  55. end
  56. 1 def dropped_item_cards
  57. return [] unless db_content_before_act
  58. all_item_cards item_names: dropped_item_names
  59. end
  60. 1 def added_item_cards
  61. return item_cards unless db_content_before_act
  62. all_item_cards item_names: added_item_names
  63. end
  64. end;end;end;end;end;
  65. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/events.rb ~~

card/tmpsets/set/mod005-pointer/abstract/02_pointer/html_views.rb

65.57% lines covered

61 relevant lines. 40 lines covered and 21 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Pointer;
  3. # Set: Abstract (Pointer, HtmlViews)
  4. #
  5. 1 module HtmlViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/html_views.rb"; end
  8. 1 include_set Abstract::BsBadge
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 view :core, cache: :never do
  11. 16 standard_pointer_core
  12. end
  13. 1 view :item_cores, cache: :never do
  14. card.known_item_cards.map do |item|
  15. nest item, view: :core
  16. end.join "\n"
  17. end
  18. 1 def standard_pointer_core
  19. 16 with_paging do |paging_args|
  20. 16 wrap_with :div, standard_pointer_items(paging_args), class: "pointer-list"
  21. end
  22. end
  23. 1 def standard_pointer_items paging_args
  24. 16 pointer_items(paging_args.extract!(:limit, :offset)).join(voo.separator || "\n")
  25. end
  26. 1 view :one_line_content do
  27. item_view = implicit_item_view
  28. item_view = item_view == "name" ? "name" : "link"
  29. wrap_with :div, class: "pointer-list" do
  30. # limit to first 10 items to optimize
  31. pointer_items(view: item_view, limit: 10, offset: 0).join ", "
  32. end
  33. end
  34. 1 def wrap_item rendered, item_view
  35. 19 %(<div class="pointer-item item-#{item_view}">#{rendered}</div>)
  36. end
  37. 1 view :input do
  38. 194 _render_hidden_content_field + super()
  39. end
  40. 1 def default_input_type
  41. :list
  42. end
  43. 1 view :list, cache: :never do
  44. list_input
  45. end
  46. # view :nav_item do
  47. # nav_dropdown
  48. # end
  49. 1 def list_input args={}
  50. items = items_for_input args[:item_list]
  51. extra_class = "pointer-list-ul"
  52. ul_classes = classy "pointer-list-editor", extra_class
  53. haml :list_input, items: items, ul_classes: ul_classes,
  54. options_card: options_card_name
  55. end
  56. 1 %i[autocomplete checkbox radio select multiselect].each do |editor_view|
  57. 5 view(editor_view) { send "#{editor_view}_input" }
  58. end
  59. 1 def autocomplete_input
  60. items = items_for_input
  61. haml :autocomplete_input, item: items.first, options_card: options_card_name
  62. end
  63. 1 def checkbox_input
  64. 2 haml :checkbox_input, submit_on_change: @submit_on_change
  65. end
  66. 1 def radio_input
  67. 1 haml :radio_input, submit_on_change: @submit_on_change
  68. end
  69. 1 def select_input
  70. options = { "-- Select --" => "" }.merge card.options_hash
  71. select_tag("pointer_select-#{unique_id}",
  72. options_for_select(options, card.item_name),
  73. class: "pointer-select form-control")
  74. end
  75. 1 def multiselect_input
  76. 1 select_tag "pointer_multiselect-#{unique_id}",
  77. options_for_select(card.options_hash, card.item_names),
  78. multiple: true, class: "pointer-multiselect form-control"
  79. end
  80. 1 def add_item_modal_link
  81. modal_link "Add Item",
  82. size: :large,
  83. class: "btn btn-sm btn-outline-secondary _add-item-link",
  84. path: { view: :filter_items_modal,
  85. item: implicit_item_view,
  86. filter_card: filter_card.name,
  87. slot_selector: filtered_list_slot_class,
  88. item_selector: "_filtered-list-item",
  89. slot: { hide: [:modal_footer] },
  90. filter: { not_ids: not_ids_value } }
  91. end
  92. 1 def not_ids_value
  93. card.item_ids.map(&:to_s).join(",")
  94. end
  95. 1 def add_item_overlay_link; end
  96. 1 def one_line_content
  97. 1 if count == 1
  98. card.first_name
  99. else
  100. 1 short_content
  101. end
  102. end
  103. 1 private
  104. # currently only used by :list and :autocomplete. could be generalized?
  105. 1 def items_for_input items=nil
  106. items ||= card.item_names context: :raw
  107. items.empty? ? [""] : items
  108. end
  109. end
  110. end;end;end;end;end;
  111. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/html_views.rb ~~

card/tmpsets/set/mod005-pointer/abstract/02_pointer/html_views/filter.rb

46.88% lines covered

32 relevant lines. 15 lines covered and 17 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 5 class Card; module Set; class Abstract; module Pointer;; module HtmlViews;
  3. # Set: Abstract (Pointer, HtmlViews, Filter)
  4. #
  5. 1 module Filter;
  6. 1 extend Card::Set
  7. 2 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/html_views/filter.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :filtered_list, unknown: true do
  10. filtered_list_input
  11. end
  12. 1 view :filter_items_modal, unknown: true, wrap: :modal do
  13. render_filter_items
  14. end
  15. 1 view :filter_items, unknown: true, wrap: :slot, template: :haml
  16. 1 def filtered_list_input
  17. with_nest_mode :normal do
  18. class_up "card-slot", filtered_list_slot_class
  19. with_class_up "card-slot", filtered_list_slot_class do
  20. wrap do
  21. haml :filtered_list_input
  22. end
  23. end
  24. end
  25. end
  26. # NOCACHE because params alter view
  27. 1 view :add_selected_link, cache: :never, unknown: true do
  28. link_to "Add Selected",
  29. path: { filter_card: params[:filter_card] },
  30. class: "_add-selected slotter _close-modal btn btn-primary disabled",
  31. data: { "slot-selector": ".#{params[:slot_selector]}",
  32. "item-selector": ".#{params[:item_selector]}",
  33. remote: true }
  34. end
  35. 1 def filtered_list_item item_card
  36. nest_item item_card do |rendered, item_view|
  37. wrap_item rendered, item_view
  38. end
  39. end
  40. # for override
  41. # @return [Card] search card on which filtering is based
  42. 1 def filter_card
  43. filter_card_from_params || default_filter_card
  44. end
  45. 1 def default_filter_card
  46. fcard = card.options_rule_card || Card[:all]
  47. return fcard if fcard.respond_to? :cql_hash
  48. fcard.fetch :referred_to_by, new: {}
  49. end
  50. 1 def filter_card_from_params
  51. return unless params[:filter_card]
  52. Card.fetch params[:filter_card], new: {}
  53. end
  54. # currently actually used as a class
  55. # (because we don't have api to override slot's id)
  56. 1 def filtered_list_slot_class
  57. @filtered_list_slot_class ||= "filtered-list-#{unique_id}"
  58. end
  59. end
  60. end;end;end;end;end;end;
  61. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/html_views/filter.rb ~~

card/tmpsets/set/mod005-pointer/abstract/02_pointer/options_api.rb

86.05% lines covered

43 relevant lines. 37 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Pointer;
  3. # Set: Abstract (Pointer, OptionsApi)
  4. #
  5. # TODO: some of this should be moved to right/options!!
  6. 1 module OptionsApi;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/options_api.rb"; end
  9. # or to type/JSON?
  10. 1 def options_hash
  11. 4 json_options? ? options_card.parse_content : option_hash_from_names
  12. end
  13. 1 def json_options?
  14. 8 options_card&.type_id == JsonID
  15. end
  16. 1 def option_hash_from_names
  17. 4 option_names.each_with_object({}) do |name, hash|
  18. 56 hash[name] = name
  19. end
  20. end
  21. 1 def option_names
  22. 4 if (selected_options = item_names)
  23. 4 (standard_option_names + selected_options).uniq
  24. else
  25. standard_option_names
  26. end
  27. end
  28. 1 def option_cards
  29. option_names.map do |name|
  30. Card.fetch name, new: {}
  31. end
  32. end
  33. 1 def options_rule_card
  34. 54 rule_card :content_options
  35. end
  36. 1 def standard_option_names
  37. 4 if json_options?
  38. options_hash.values.map(&:to_name)
  39. else
  40. 4 option_names_from_items
  41. end
  42. end
  43. 1 def option_names_from_items
  44. 4 o_card = options_card
  45. 4 limit = o_card.try(:default_limit).to_i
  46. 4 o_card.item_names context: name, limit: limit
  47. end
  48. 1 def options_card
  49. 12 options_rule_card || Card[:all]
  50. end
  51. 1 def options_card_name
  52. options_rule_card&.name&.url_key || ":all"
  53. end
  54. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  55. 1 def options_card_name
  56. card.options_card_name
  57. end
  58. end
  59. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  60. 1 def option_label option_name, id
  61. 42 %(<label for="#{id}">#{option_label_text option_name}</label>)
  62. end
  63. 1 def option_view
  64. 42 @option_view ||= card.rule(:content_option_view) || :smart_label
  65. end
  66. 1 def option_label_text option_name
  67. 42 return option_name unless (option_card = Card.fetch option_name)
  68. 42 nest option_card, view: option_view
  69. end
  70. end
  71. end;end;end;end;end;
  72. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/options_api.rb ~~

card/tmpsets/set/mod005-pointer/abstract/02_pointer/other_views.rb

62.0% lines covered

50 relevant lines. 31 lines covered and 19 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Pointer;
  3. # Set: Abstract (Pointer, OtherViews)
  4. #
  5. # BASE views
  6. 1 module OtherViews;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/other_views.rb"; end
  9. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  10. 1 def default_limit
  11. 16 20
  12. end
  13. 1 def item_links args={}
  14. card.item_cards(args).map do |item_card|
  15. subformat(item_card).render_link
  16. end
  17. end
  18. 1 def nest_item_array
  19. card.item_cards.map do |item|
  20. nest_item item
  21. end
  22. end
  23. 1 view :core do
  24. pointer_items.join ", "
  25. end
  26. 1 def pointer_items args={}
  27. 16 page_args = args.extract! :limit, :offset
  28. 16 listing card.item_cards(page_args), args
  29. end
  30. end
  31. # JavaScript views
  32. 2 module JsFormat; module_parent.send :register_set_format, Card::Format::JsFormat, self; extend Card::Set::AbstractFormat
  33. 1 view :core do
  34. nest_item_array.join "\n\n"
  35. end
  36. end
  37. # Data views
  38. 2 module DataFormat; module_parent.send :register_set_format, Card::Format::DataFormat, self; extend Card::Set::AbstractFormat
  39. 1 view :core do
  40. nest_item_array
  41. end
  42. end
  43. # JSON views
  44. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  45. 1 view :content do
  46. card.item_names
  47. end
  48. 1 def item_cards
  49. card.item_cards
  50. end
  51. 1 def max_depth
  52. params[:max_depth] || 1
  53. end
  54. 1 def items_for_export
  55. card.item_cards
  56. end
  57. 1 def essentials
  58. return {} if depth > max_depth
  59. card.item_cards.map do |item|
  60. nest item, view: :essentials
  61. end
  62. end
  63. 1 view :links do
  64. []
  65. end
  66. end
  67. # CSS views
  68. 2 module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
  69. # generalize to all collections?
  70. 1 def default_item_view
  71. :content
  72. end
  73. 1 view :titled do
  74. %(#{major_comment "STYLE GROUP: \"#{card.name}\"", '='}#{_render_core})
  75. end
  76. 1 view :core do
  77. nest_item_array.join "\n\n"
  78. end
  79. 1 view :content, :core
  80. end
  81. # RSS views
  82. 2 module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
  83. 1 def raw_feed_items
  84. @raw_feed_items ||= card.item_cards(limit: limit, offset: offset)
  85. end
  86. end
  87. end;end;end;end;end;
  88. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/02_pointer/other_views.rb ~~

card/tmpsets/set/mod005-pointer/abstract/code_pointer.rb

75.0% lines covered

20 relevant lines. 15 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (CodePointer)
  4. #
  5. 1 module CodePointer;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/code_pointer.rb"; end
  8. 1 include_set Abstract::Pointer
  9. 1 abstract_basket :item_codenames
  10. # simplify api
  11. # Self::MyCodePointerSet.add_item :my_item_codename
  12. # instead of
  13. # Self::MyCodePointerSet.add_to_basket :item_codenames, :my_item_codename
  14. 1 module ClassMethods
  15. 1 def add_item codename
  16. 21 valid_codename codename do
  17. 21 add_to_basket :item_codenames, codename
  18. end
  19. end
  20. 1 def unshift_item codename
  21. valid_codename codename do
  22. unshift_basket :item_codenames, codename
  23. end
  24. end
  25. 1 def valid_codename codename
  26. 21 if Card::Codename.exist? codename
  27. 21 yield
  28. else
  29. Rails.logger.info "unknown codename '#{codename}' added to code pointer"
  30. end
  31. end
  32. end
  33. 1 def content
  34. item_codenames.map do |codename|
  35. Card.fetch_name codename
  36. end.compact.to_pointer_content
  37. end
  38. end;end;end;end;
  39. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/code_pointer.rb ~~

card/tmpsets/set/mod005-pointer/abstract/id_pointer.rb

50.0% lines covered

14 relevant lines. 7 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (IdPointer)
  4. #
  5. # store items as ids, not names
  6. 1 module IdPointer;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/id_pointer.rb"; end
  9. 1 def standardize_item cardish
  10. if (id = Card.fetch_id cardish)
  11. "~#{id}"
  12. else
  13. Rails.logger.info "no id for '#{cardish}' added to id pointer"
  14. nil
  15. end
  16. end
  17. 1 def item_ids args={}
  18. item_strings(args).map do |item|
  19. item = standardize_item item unless item.match?(/^~/)
  20. item.to_s.tr("~", "").to_i
  21. end.compact
  22. end
  23. 1 def item_names args={}
  24. item_ids(args).map(&:cardname).compact
  25. end
  26. end;end;end;end;
  27. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/abstract/id_pointer.rb ~~

card/tmpsets/set/mod005-pointer/right/content_options.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+ContentOptions" cards
  4. #
  5. 1 module ContentOptions;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/right/content_options.rb"; end
  8. 1 def default_limit
  9. 4 cql_limit = fetch_query.limit if respond_to?(:fetch_query)
  10. 4 cql_limit || 50
  11. end
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/right/content_options.rb ~~

card/tmpsets/set/mod005-pointer/self/input_options.rb

92.86% lines covered

14 relevant lines. 13 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "InputOptions"
  4. #
  5. 1 module InputOptions;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/input_options.rb"; end
  8. 1 include_set Abstract::Pointer
  9. 1 basket :options
  10. 1 add_to_basket :options, "radio"
  11. 1 add_to_basket :options, "checkbox"
  12. 1 add_to_basket :options, "select"
  13. 1 add_to_basket :options, "multiselect"
  14. 1 add_to_basket :options, "list"
  15. 1 add_to_basket :options, "filtered list"
  16. 1 def content
  17. options.to_pointer_content
  18. end
  19. end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/input_options.rb ~~

card/tmpsets/set/mod005-pointer/self/script_editors.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptEditors"
  4. #
  5. 1 module ScriptEditors;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_editors.rb"; end
  8. 1 include_set Abstract::CodePointer
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_editors.rb ~~

card/tmpsets/set/mod005-pointer/self/script_libraries.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptLibraries"
  4. #
  5. 1 module ScriptLibraries;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_libraries.rb"; end
  8. 1 include_set Abstract::CodePointer
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_libraries.rb ~~

card/tmpsets/set/mod005-pointer/self/script_mods.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptMods"
  4. #
  5. 1 module ScriptMods;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_mods.rb"; end
  8. 1 include_set Abstract::CodePointer
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_mods.rb ~~

card/tmpsets/set/mod005-pointer/self/script_pointer_config.rb

88.89% lines covered

9 relevant lines. 8 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptPointerConfig"
  4. #
  5. 1 module ScriptPointerConfig;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_pointer_config.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 FILE_NAMES = %w[pointer_config pointer_list_editor]
  10. 1 def source_files
  11. coffee_files FILE_NAMES
  12. end
  13. 1 Self::ScriptEditors.add_item :script_pointer_config
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/script_pointer_config.rb ~~

card/tmpsets/set/mod005-pointer/self/style_libraries.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleLibraries"
  4. #
  5. 1 module StyleLibraries;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/style_libraries.rb"; end
  8. 1 include_set Abstract::CodePointer
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/style_libraries.rb ~~

card/tmpsets/set/mod005-pointer/self/style_mods.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleMods"
  4. #
  5. 1 module StyleMods;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/self/style_mods.rb"; end
  8. 1 include_set Abstract::CodePointer
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/self/style_mods.rb ~~

card/tmpsets/set/mod005-pointer/type/link_list.rb

53.85% lines covered

26 relevant lines. 14 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "LinkList" cards
  4. #
  5. 1 module LinkList;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/link_list.rb"; end
  8. 1 include_set Abstract::Pointer
  9. 1 def raw_item_strings content
  10. reference_chunks(content).map(&:referee_name)
  11. end
  12. 1 def item_titles default_to_name=true
  13. reference_chunks.map do |chunk|
  14. chunk.options[:title] || (default_to_name ? chunk.referee_name : nil)
  15. end
  16. end
  17. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  18. 1 def chunk_list
  19. :references
  20. end
  21. end
  22. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  23. 1 def input_type
  24. :link_list
  25. end
  26. 1 view :link_list_input, cache: :never do
  27. link_list_input
  28. end
  29. 1 def items_for_input items=nil
  30. items ||= card.item_names context: :raw
  31. items.empty? ? [["", ""]] : items.zip(card.item_titles(false))
  32. end
  33. 1 def link_list_input args={}
  34. items = items_for_input args[:item_list]
  35. extra_class = "pointer-link-list-ul"
  36. ul_classes = classy "pointer-list-editor", extra_class
  37. haml :link_list_input, items: items, ul_classes: ul_classes,
  38. options_card: options_card_name
  39. end
  40. end
  41. end;end;end;end;
  42. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/link_list.rb ~~

card/tmpsets/set/mod005-pointer/type/list.rb

76.92% lines covered

13 relevant lines. 10 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "List" cards
  4. #
  5. 1 module List;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/list.rb"; end
  8. 1 include_set Abstract::Pointer
  9. 1 def each_reference_out
  10. 53 item_names.each do |name|
  11. 475 yield(name, Card::Content::Chunk::Link::CODE)
  12. end
  13. end
  14. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  15. 1 view :view_list do
  16. %i[info_bar bar box closed titled labeled].map do |view|
  17. voo.items[:view] = view
  18. wrap_with :p, [content_tag(:h3, "#{view} items"), render_content]
  19. end
  20. end
  21. end
  22. end;end;end;end;
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/list.rb ~~

card/tmpsets/set/mod005-pointer/type/mirror_list.rb

36.73% lines covered

49 relevant lines. 18 lines covered and 31 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "MirrorList" cards
  4. #
  5. 1 module MirrorList;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/mirror_list.rb"; end
  8. 1 include_set Abstract::Pointer
  9. 1 event :validate_listed_by_name, :validate, on: :save, changing: :name do
  10. if !junction? || !right || right.type_id != Card::CardtypeID
  11. errors.add :name, tr(:cardtype_right)
  12. end
  13. end
  14. 1 event :validate_listed_by_content, :validate,
  15. on: :save, changing: :content do
  16. item_cards(content: content).each do |item_card|
  17. next unless item_card.type_id != right.id
  18. errors.add(
  19. :content,
  20. "#{item_card.name} has wrong cardtype; " \
  21. "only cards of type #{name.right} are allowed"
  22. )
  23. end
  24. end
  25. 1 event :update_content_in_list_cards, :prepare_to_validate,
  26. on: :save, changing: :content do
  27. return unless db_content.present?
  28. new_items = item_keys(content: db_content)
  29. old_items = item_keys(content: old_content)
  30. remove_items(old_items - new_items)
  31. add_items(new_items - old_items)
  32. end
  33. 1 def old_content
  34. db_content_before_act.present? ? db_content_before_act : content_cache.read(key)
  35. end
  36. 1 def remove_items items
  37. items.each do |item|
  38. next unless (lc = list_card item)
  39. lc.drop_item name.left
  40. subcards.add lc
  41. end
  42. end
  43. 1 def add_items items
  44. items.each do |item|
  45. if (lc = list_card(item))
  46. lc.add_item name.left
  47. subcards.add lc
  48. else
  49. subcards.add(name: "#{Card[item].name}+#{left.type_name}",
  50. type: "list",
  51. content: "[[#{name.left}]]")
  52. end
  53. end
  54. end
  55. 1 def content_cache
  56. Card::Cache[Card::Set::Type::MirrorList]
  57. end
  58. 1 def content
  59. content_cache.fetch(key) do
  60. generate_content
  61. end
  62. end
  63. 1 def generate_content
  64. listed_by.map do |item|
  65. "[[%s]]" % item.to_name.left
  66. end.join "\n"
  67. end
  68. 1 def listed_by
  69. Card.search(
  70. { type_id: Card::MirroredListID, right: trunk.type_name,
  71. left: { type: name.tag }, refer_to: name.trunk, return: :name },
  72. "all cards listed by #{name}"
  73. )
  74. end
  75. 1 def update_cached_list
  76. if trunk
  77. Card::Cache[Card::Set::Type::MirrorList].write key, generate_content
  78. else
  79. Card::Cache[Card::Set::Type::MirrorList].delete key
  80. end
  81. end
  82. 1 def list_card item
  83. Card.fetch item, left.type_name
  84. end
  85. 1 def unfilled?
  86. false
  87. end
  88. end;end;end;end;
  89. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/mirror_list.rb ~~

card/tmpsets/set/mod005-pointer/type/mirrored_list.rb

31.75% lines covered

63 relevant lines. 20 lines covered and 43 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "MirroredList" cards
  4. #
  5. 1 module MirroredList;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/mirrored_list.rb"; end
  8. 1 include_set Abstract::Pointer
  9. 1 event :validate_list_name, :validate, on: :save, changed: :name do
  10. errors.add :name, tr(:cardtype_right) unless right&.type_id == Card::CardtypeID
  11. end
  12. 1 event :validate_list_item_type_change, :validate,
  13. on: :save, changed: :name do
  14. item_cards.each do |item_card|
  15. next unless item_card.type_name.key != item_type_name.key
  16. errors.add :name, tr(:conflict_item_type)
  17. end
  18. end
  19. 1 event :validate_list_content, :validate,
  20. on: :save, changed: :content do
  21. item_cards.each do |item_card|
  22. next unless item_card.type_name.key != item_type_name.key
  23. errors.add :content, tr(
  24. :only_type_allowed,
  25. cardname: item_card.name,
  26. cardtype: name.right
  27. )
  28. end
  29. end
  30. 1 event :create_listed_by_cards, :prepare_to_validate,
  31. on: :save, changed: :content do
  32. item_names.each do |item_name|
  33. listed_by_name = "#{item_name}+#{left.type_name}"
  34. next if director.main_director.card.key == listed_by_name.to_name.key
  35. if !Card[listed_by_name]
  36. add_subcard listed_by_name, type_id: Card::MirrorListID
  37. else
  38. Card[listed_by_name].update_references_out
  39. end
  40. end
  41. end
  42. 1 event :update_related_listed_by_card_on_create, :finalize,
  43. on: :create do
  44. update_listed_by_cache_for item_keys
  45. end
  46. 1 event :update_related_listed_by_card_on_content_update, :finalize,
  47. on: :update, changed: :content do
  48. new_items = item_keys
  49. changed_items =
  50. if db_content_before_act
  51. old_items = item_keys(content: db_content_before_act)
  52. old_items + new_items - (old_items & new_items)
  53. else
  54. new_items
  55. end
  56. update_listed_by_cache_for changed_items
  57. end
  58. 1 event :update_related_listed_by_card_on_name_and_type_changes, :finalize,
  59. on: :update, changed: %i[name type_id] do
  60. update_all_items
  61. end
  62. 1 event :update_related_listed_by_card_on_delete, :finalize,
  63. on: :delete, when: proc { |c| c.junction? } do
  64. update_listed_by_cache_for item_keys, type_key: @left_type_key
  65. end
  66. 1 event :cache_type_key, :store,
  67. on: :delete, when: proc { |c| c.junction? } do
  68. @left_type_key = left.type_card.key
  69. end
  70. 1 def update_all_items
  71. current_items = item_keys
  72. if db_content_before_act
  73. old_items = item_keys(content: db_content_before_act)
  74. update_listed_by_cache_for old_items
  75. end
  76. update_listed_by_cache_for current_items
  77. end
  78. 1 def update_listed_by_cache_for item_keys, args={}
  79. type_key = args[:type_key] || left&.type_card&.key
  80. return unless type_key
  81. item_keys.each do |item_key|
  82. key = "#{item_key}+#{type_key}"
  83. next unless Card::Cache[Card::Set::Type::MirrorList].exist? key
  84. if (card = Card.fetch(key)) && card.left
  85. card.update_cached_list
  86. card.update_references_out
  87. else
  88. Card::Cache[Card::Set::Type::MirrorList].delete key
  89. end
  90. end
  91. end
  92. 1 def item_type
  93. name.right
  94. end
  95. 1 def item_type_name
  96. name.right_name
  97. end
  98. 1 def item_type_card
  99. name.right
  100. end
  101. 1 def item_type_id
  102. right.id
  103. end
  104. end;end;end;end;
  105. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/mirrored_list.rb ~~

card/tmpsets/set/mod005-pointer/type/nest_list.rb

53.33% lines covered

30 relevant lines. 16 lines covered and 14 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "NestList" cards
  4. #
  5. 1 module NestList;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/nest_list.rb"; end
  8. 1 include_set Abstract::Items
  9. 1 def raw_item_strings content
  10. reference_chunks(content).map(&:referee_name)
  11. end
  12. 1 def item_options
  13. nest_chunks.map(&:raw_options)
  14. end
  15. 1 def items_to_content array
  16. items = array.map { |i| standardize_item i }.reject(&:blank?)
  17. self.content = items.join("\n")
  18. end
  19. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  20. 1 def chunk_list
  21. :references
  22. end
  23. end
  24. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  25. 1 def input_type
  26. :nest_list
  27. end
  28. 1 view :nest_list_input, cache: :never do
  29. nest_list_input
  30. end
  31. 1 view :input do
  32. _render_hidden_content_field + super()
  33. end
  34. 1 def items_for_input items=nil
  35. items ||= card.item_names context: :raw
  36. items.empty? ? [["", ""]] : items.zip(card.item_options)
  37. end
  38. 1 def nest_list_input args={}
  39. items = items_for_input args[:item_list]
  40. extra_class = "_nest-list-ul"
  41. ul_classes = classy "pointer-list-editor", extra_class
  42. haml :nest_list_input, items: items, ul_classes: ul_classes
  43. end
  44. end
  45. end;end;end;end;
  46. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/nest_list.rb ~~

card/tmpsets/set/mod005-pointer/type/pointer.rb

70.0% lines covered

10 relevant lines. 7 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Pointer" cards
  4. #
  5. 1 module Pointer;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/pointer/set/type/pointer.rb"; end
  8. 1 include_set Abstract::Pointer
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 view :view_list do
  11. %i[info_bar bar box closed titled labeled].map do |view|
  12. voo.items[:view] = view
  13. wrap_with :p, [content_tag(:h3, "#{view} items"), render_content]
  14. end
  15. end
  16. end
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/pointer/set/type/pointer.rb ~~

card/tmpsets/set/mod006-card-mod-virtual/abstract/virtual_cache.rb

54.17% lines covered

24 relevant lines. 13 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (VirtualCache)
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module VirtualCache;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-virtual/set/abstract/virtual_cache.rb"; end
  9. 1 def virtual?
  10. new?
  11. end
  12. 1 def history?
  13. false
  14. end
  15. 1 def followable?
  16. false
  17. end
  18. 1 def db_content
  19. Card::Virtual.fetch_content(self)
  20. end
  21. # called to refresh the virtual content
  22. # the default way is to use the card's template content
  23. 1 def generate_virtual_content
  24. template&.db_content
  25. end
  26. 1 event :save_virtual_content, :prepare_to_store, on: :save, changed: :content do
  27. Card::Virtual.create_or_update(self, attributes["db_content"])
  28. abort :success
  29. end
  30. 1 event :delete_virtual_content, :prepare_to_store, on: :delete do
  31. Card::Virtual.find_by_card(self)&.delete
  32. abort :success
  33. end
  34. 1 def delete
  35. # delete although it's new
  36. update trash: true
  37. end
  38. 1 def delete!
  39. # delete although it's new
  40. update! trash: true
  41. end
  42. end;end;end;end;
  43. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-virtual/set/abstract/virtual_cache.rb ~~

card/tmpsets/set/mod007-card-mod-machines/abstract/machine.rb

45.05% lines covered

111 relevant lines. 50 lines covered and 61 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Machine)
  4. #
  5. # ## What are Machines?
  6. # {Machine} and {MachineInput} together implement a kind of observer pattern.
  7. # {Machine} processes a collection of input cards to generate an output card
  8. # (a {Set::Type::File} card by default). If one of the input cards is changed
  9. # the output card will be updated.
  10. #
  11. # The classic example: A style card observes a collection of css and sccs card
  12. # to generate a file card with a css file that contains the assembled
  13. # compressed css.
  14. #
  15. # ## Using Machines
  16. # Include the Machine module in the card set that is supposed to produce the
  17. # output card. If the output card should be automatically updated when a input
  18. # card is changed the input card has to be in a set that includes the
  19. # MachineInput module.
  20. #
  21. # The default machine:
  22. 1 module Machine;
  23. 1 extend Card::Set
  24. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine.rb"; end
  25. #
  26. # - uses its item cards as input cards or the card itself if there are no
  27. # item cards;
  28. # - can be changed by passing a block to collect_input_cards
  29. # - takes the raw view of the input cards to generate the output;
  30. # - can be changed by passing a block to machine_input (in the input card
  31. # set)
  32. # - stores the output as a .txt file in the '+machine output' card;
  33. # - can be changed by passing a filetype and/or a block to
  34. # store_machine_output
  35. #
  36. #
  37. # ## How does it work?
  38. # Machine cards have a '+machine input' and a '+machine output' card. The
  39. # '+machine input' card is a pointer to all input cards. Including the
  40. # MachineInput module creates an 'on: save' event that runs the machines of
  41. # all cards that are linked to that card via the +machine input pointer.
  42. 1 module MachineClassMethods
  43. 1 attr_accessor :output_config
  44. 1 def collect_input_cards &block
  45. define_method :engine_input, &block
  46. end
  47. 1 def prepare_machine_input &block
  48. define_method :before_engine, &block
  49. end
  50. 1 def machine_engine &block
  51. define_method :engine, &block
  52. end
  53. 1 def store_machine_output args={}, &block
  54. 5 output_config.merge!(args)
  55. 5 return unless block_given?
  56. define_method :after_engine, &block
  57. end
  58. end
  59. 1 card_accessor :machine_output, type: FileID
  60. 1 card_accessor :machine_input, type: PointerID
  61. 1 def before_engine
  62. end
  63. 1 def engine_input
  64. ei = EngineInput.new self
  65. ei.process
  66. ei.new_input
  67. end
  68. # TODO: replace with call of extended_item_cards
  69. # traverse through all levels of pointers and
  70. # collect all item cards as input
  71. 1 class EngineInput
  72. 1 attr_accessor :new_input
  73. 1 def initialize machine_card
  74. @machine_card = machine_card
  75. @items = [machine_card]
  76. @new_input = []
  77. @extended = {}
  78. @loop_limit = 5
  79. end
  80. 1 def process
  81. each_valid_item do
  82. input_item = simple_item? ? @item : pointer_item
  83. new_input << input_item if input_item
  84. end
  85. end
  86. 1 def simple_item?
  87. @item.item_cards == [@item] # no pointer card
  88. end
  89. 1 def pointer_item
  90. @items.insert 0, @item.item_cards.reject(&:unknown?)
  91. @items.flatten!
  92. record_item
  93. @item if @item != @machine_card && @item.known?
  94. end
  95. 1 def record_item
  96. @extended[@item] = @extended[@item].to_i + 1
  97. end
  98. 1 def each_valid_item
  99. until @items.empty?
  100. @item = @items.shift
  101. yield unless invalid_item?
  102. end
  103. end
  104. 1 def invalid_item?
  105. @item.trash || @extended[@item.id].to_i > @loop_limit
  106. end
  107. end
  108. 1 def engine input
  109. input
  110. end
  111. 1 def after_engine output
  112. filetype = output_config[:filetype]
  113. file = Tempfile.new [id.to_s, ".#{filetype}"]
  114. file.write output
  115. file.rewind
  116. Card::Auth.as_bot do
  117. p = machine_output_card
  118. p.file = file
  119. p.save!
  120. end
  121. file.close
  122. file.unlink
  123. end
  124. 1 view :machine_output_url do
  125. machine_output_url
  126. end
  127. 1 class << self
  128. 1 def included host_class
  129. 6 host_class.extend(MachineClassMethods)
  130. 6 host_class.mattr_accessor :output_config
  131. 6 host_class.output_config = { filetype: "txt" }
  132. 6 define_machine_events host_class
  133. end
  134. 1 def define_machine_events host_class
  135. 6 event_suffix = host_class.name.tr ":", "_"
  136. 6 event_name = "reset_machine_output_#{event_suffix}".to_sym
  137. 6 host_class.event event_name, after: :expire_related, changed: :content, on: :save do
  138. reset_machine_output
  139. end
  140. end
  141. end
  142. 1 include_set Abstract::Lock
  143. 1 def run_machine joint="\n"
  144. before_engine
  145. output =
  146. input_item_cards.map do |input_card|
  147. run_engine input_card
  148. end.select(&:present?).join(joint)
  149. after_engine output
  150. end
  151. 1 def direct_machine_input? input_card
  152. !input_card.collection? ||
  153. input_card.respond_to?(:machine_input)
  154. end
  155. 1 def run_engine input_card
  156. return unless direct_machine_input? input_card
  157. if (cached = fetch_cache_card(input_card)) && cached.content?
  158. return cached.content
  159. end
  160. engine(input_from_card(input_card)).tap do |output|
  161. cache_output_part input_card, output
  162. end
  163. end
  164. 1 def input_from_card input_card
  165. if input_card.respond_to? :machine_input
  166. input_card.machine_input
  167. else
  168. input_card.format._render_raw
  169. end
  170. end
  171. 1 def make_machine_output_coded mod=:machines
  172. update_machine_output
  173. Card::Auth.as_bot do
  174. ENV["STORE_CODED_FILES"] = "true"
  175. machine_output_card.update! storage_type: :coded, mod: mod,
  176. codename: machine_output_codename
  177. ENV["STORE_CODED_FILES"] = nil
  178. end
  179. end
  180. 1 def machine_output_codename
  181. machine_output_card.name.parts.map do |part|
  182. Card[part].codename&.to_s || Card[part].name.safe_key
  183. end.join "_"
  184. end
  185. 1 def input_item_cards
  186. machine_input_card.item_cards
  187. end
  188. 1 def machine_output_url
  189. 896 ensure_machine_output
  190. 896 machine_output_card.file.url # (:default, timestamp: false)
  191. # to get rid of additional number in url
  192. end
  193. 1 def machine_output_path
  194. ensure_machine_output
  195. machine_output_card.file.path
  196. end
  197. end;end;end;end;
  198. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine.rb ~~

card/tmpsets/set/mod007-card-mod-machines/abstract/machine/output_cache.rb

50.0% lines covered

12 relevant lines. 6 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Machine;
  3. # Set: Abstract (Machine, OutputCache)
  4. #
  5. 1 module OutputCache;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine/output_cache.rb"; end
  8. 1 def fetch_cache_card input_card, new=nil
  9. new &&= { type_id: PlainTextID }
  10. Card.fetch input_card.name, name, :machine_cache, new: new
  11. end
  12. 1 def cache_output_part input_card, output
  13. Auth.as_bot do
  14. # save virtual cards first
  15. # otherwise the cache card will save it to get the left_id
  16. # and trigger the cache update again
  17. input_card.save! if input_card.new_card?
  18. cache_card = fetch_cache_card(input_card, true)
  19. cache_card.update! content: output
  20. end
  21. end
  22. end;end;end;end;end;
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine/output_cache.rb ~~

card/tmpsets/set/mod007-card-mod-machines/abstract/machine/output_update.rb

31.91% lines covered

47 relevant lines. 15 lines covered and 32 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Machine;
  3. # Set: Abstract (Machine, OutputUpdate)
  4. #
  5. 1 module OutputUpdate;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine/output_update.rb"; end
  8. 1 def reset_machine_output
  9. Auth.as_bot do
  10. moc = machine_output_card
  11. @updated_at = output_updated_at
  12. moc.delete! if moc.real?
  13. update_input_card
  14. expire_if_source_file_changed @updated_at
  15. end
  16. end
  17. 1 def regenerate_machine_output
  18. return unless ok?(:read)
  19. lock { run_machine }
  20. end
  21. 1 def update_machine_output
  22. return unless ok?(:read)
  23. lock do
  24. update_input_card
  25. expire_if_source_file_changed output_updated_at
  26. run_machine
  27. end
  28. end
  29. 1 def ensure_machine_output
  30. 896 output = fetch :machine_output
  31. 896 return if output&.selected_content_action_id
  32. update_machine_output
  33. end
  34. 1 def update_input_card
  35. if Card::Director.running_act?
  36. input_card = attach_subcard! machine_input_card
  37. input_card.content = ""
  38. engine_input.each { |input| input_card << input }
  39. else
  40. machine_input_card.items = engine_input
  41. end
  42. end
  43. 1 def input_cards_with_changed_source output_updated
  44. machine_input_card.extended_item_cards.select do |i_card|
  45. i_card.try(:source_changed?, since: output_updated)
  46. end
  47. end
  48. 1 def expire_if_source_file_changed output_updated_at
  49. return unless output_updated_at
  50. changed = input_cards_with_changed_source(output_updated_at)
  51. return if changed.empty?
  52. changed.each(&:expire_machine_cache)
  53. true
  54. end
  55. # regenerates the machine output if a source file of a input card has been changed
  56. 1 def update_if_source_file_changed
  57. return unless expire_if_source_file_changed output_updated_at
  58. regenerate_machine_output
  59. end
  60. 1 def output_updated_at
  61. return unless (output_card = machine_output_card)
  62. if output_card.coded?
  63. File.mtime output_card.file.path
  64. else
  65. output_card.updated_at
  66. end
  67. end
  68. end;end;end;end;end;
  69. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine/output_update.rb ~~

card/tmpsets/set/mod007-card-mod-machines/abstract/machine_input.rb

60.98% lines covered

41 relevant lines. 25 lines covered and 16 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (MachineInput)
  4. #
  5. 1 module MachineInput;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine_input.rb"; end
  8. 1 module MachineInputClassMethods
  9. 1 attr_accessor :machines_cql
  10. 1 def machine_input_for args
  11. @machines_cql = args
  12. end
  13. 1 def machine_input &block
  14. 7 define_method :machine_input, block
  15. end
  16. end
  17. 1 def self.included host_class
  18. 4 host_class.extend(MachineInputClassMethods)
  19. 4 host_class.machines_cql = {}
  20. 4 host_class.machine_input do
  21. format._render_raw
  22. end
  23. 4 event_suffix = host_class.name.tr ":", "_"
  24. 4 define_update_event event_suffix, host_class
  25. 4 define_delete_events event_suffix, host_class
  26. end
  27. 1 def self.define_delete_events event_suffix, host_class
  28. 4 event_name = "before_machine_input_deleted_#{event_suffix}".to_sym
  29. 4 host_class.event event_name, :store, on: :delete do
  30. # exclude self because it's on the way to the trash
  31. # otherwise it will be created again with the reset_machine_output
  32. # call in the event below
  33. @involved_machines =
  34. MachineInput.search_involved_machines(name, host_class)
  35. .reject { |card| card == self }
  36. end
  37. 4 event_name = "after_machine_input_deleted_#{event_suffix}".to_sym
  38. 4 host_class.event event_name, :finalize, on: :delete do
  39. expire_machine_cache
  40. @involved_machines.each do |item|
  41. item.reset_machine_output if item.is_a? Machine
  42. end
  43. end
  44. end
  45. 1 def self.define_update_event event_suffix, host_class
  46. 4 host_class.event(
  47. "after_machine_input_updated_#{event_suffix}".to_sym, :integrate,
  48. on: :save
  49. ) do
  50. expire_machine_cache
  51. MachineInput.search_involved_machines(name, host_class)
  52. .each do |item|
  53. item.reset_machine_output if item.is_a? Machine
  54. end
  55. end
  56. end
  57. 1 def self.search_involved_machines name, host_class
  58. cql_statement =
  59. { right_plus: [
  60. { codename: "machine_input" },
  61. { link_to: name }
  62. ] }.merge(host_class.machines_cql)
  63. Card.search(cql_statement)
  64. end
  65. 1 def expire_machine_cache
  66. Card.search(right_plus: [{ codename: "machine_input" }, { link_to: name }],
  67. return: :name).each do |machine_name|
  68. cache_card = Card.fetch(name, machine_name, :machine_cache)
  69. next unless cache_card&.content?
  70. Auth.as_bot { cache_card.update! trash: true }
  71. end
  72. end
  73. end;end;end;end;
  74. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/machine_input.rb ~~

card/tmpsets/set/mod007-card-mod-machines/abstract/script.rb

56.82% lines covered

44 relevant lines. 25 lines covered and 19 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Script)
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module Script;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/script.rb"; end
  9. 1 require "uglifier"
  10. 1 def self.included host_class
  11. 2 host_class.include_set Abstract::Machine
  12. 2 host_class.include_set Abstract::MachineInput
  13. 2 host_class.machine_input { standard_machine_input }
  14. 2 host_class.store_machine_output filetype: "js"
  15. end
  16. 1 def standard_machine_input
  17. js = format(:js)._render_core
  18. js = compress_js js if compress_js?
  19. comment_with_source js
  20. end
  21. 1 def comment_with_source js
  22. "//#{name}\n#{js}"
  23. end
  24. 1 def compress_js input
  25. Uglifier.compile input
  26. rescue => e
  27. # CoffeeScript is compiled in a view
  28. # If there is a CoffeeScript syntax error we get the rescued view here
  29. # and the error that the rescued view is no valid Javascript
  30. # To get the original error we have to refer to Card::Error.current
  31. raise Card::Error, compression_error_message(e)
  32. end
  33. 1 def compression_error_message e
  34. if Card::Error.current
  35. Card::Error.current.message
  36. else
  37. "JavaScript::SyntaxError (#{name}): #{e.message}"
  38. end
  39. end
  40. 1 def compress_js?
  41. Cardio.config.compress_javascript
  42. end
  43. 1 def clean_html?
  44. false
  45. end
  46. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  47. 1 def chunk_list # turn off autodetection of uri's
  48. :nest_only
  49. end
  50. # def default_nest_view
  51. # :raw
  52. # end
  53. end
  54. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  55. 1 def input_type
  56. :ace_editor
  57. end
  58. 1 def ace_mode
  59. :javascript
  60. end
  61. 1 def content_changes action, diff_type, hide_diff=false
  62. wrap_with(:pre) { super }
  63. end
  64. 1 view :core do
  65. script = card.format(:js).render_core
  66. process_content highlight(script)
  67. end
  68. 1 def highlight script
  69. ::CodeRay.scan(script, :js).div
  70. end
  71. end
  72. 1 def diff_args
  73. { diff_format: :text }
  74. end
  75. end;end;end;end;
  76. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/script.rb ~~

card/tmpsets/set/mod007-card-mod-machines/abstract/skin_box.rb

66.67% lines covered

21 relevant lines. 14 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (SkinBox)
  4. #
  5. 1 module SkinBox;
  6. 1 extend Card::Set
  7. 2 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/skin_box.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :box do
  10. class_up "box-middle", "p-0"
  11. voo.hide :customize_button, :box_middle
  12. super()
  13. end
  14. 1 view :box_bottom, template: :haml
  15. 1 view :customize_button, cache: :never do
  16. customize_button
  17. end
  18. 1 def customize_button target: parent&.card, text: "Apply and customize"
  19. 7 return "" unless card.codename.present?
  20. 7 theme = card.codename.match(/^(?<theme_name>.+)_skin$/).capture(:theme_name)
  21. 7 link_to_card target, text,
  22. path: { action: :update, card: { content: "[[#{card.name}]]" },
  23. customize: true, theme: theme },
  24. class: "btn btn-sm btn-outline-primary mr-2"
  25. end
  26. 1 view :box_middle do
  27. return unless card.field(:image)
  28. field_nest(:image, view: :full_width, size: :large)
  29. end
  30. 1 def select_button target=parent.card
  31. link_to_card target, "Apply",
  32. path: { action: :update, card: { content: "[[#{card.name}]]" } },
  33. class: "btn btn-sm btn-primary"
  34. end
  35. end
  36. end;end;end;end;
  37. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/abstract/skin_box.rb ~~

card/tmpsets/set/mod007-card-mod-machines/all/reset_machines.rb

38.89% lines covered

18 relevant lines. 7 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (ResetMachines)
  4. #
  5. 1 module ResetMachines;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/all/reset_machines.rb"; end
  8. 1 module ClassMethods
  9. 1 def reset_script_machine
  10. Auth.as_bot do
  11. card = Card[:all, :script, :machine_output]
  12. if card
  13. card.update_columns trash: true
  14. card.expire
  15. Card::Virtual.where(right_id: MachineCacheID).delete_all
  16. end
  17. end
  18. end
  19. 1 def reset_all_machines
  20. Auth.as_bot do
  21. Card.search(right: { codename: "machine_output" }).each do |card|
  22. card.update_columns trash: true
  23. card.expire
  24. end
  25. Card::Virtual.where(right_id: MachineCacheID).delete_all
  26. end
  27. end
  28. end
  29. end;end;end;end;
  30. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/all/reset_machines.rb ~~

card/tmpsets/set/mod007-card-mod-machines/right/machine_cache.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+MachineCache" cards
  4. #
  5. 1 module MachineCache;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_cache.rb"; end
  8. 1 include_set Abstract::VirtualCache
  9. 1 def clean_html?
  10. false
  11. end
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_cache.rb ~~

card/tmpsets/set/mod007-card-mod-machines/right/machine_input.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+MachineInput" cards
  4. #
  5. 1 module MachineInput;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_input.rb"; end
  8. 1 def followable?
  9. false
  10. end
  11. 1 def history?
  12. false
  13. end
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_input.rb ~~

card/tmpsets/set/mod007-card-mod-machines/right/machine_output.rb

44.44% lines covered

27 relevant lines. 12 lines covered and 15 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+MachineOutput" cards
  4. #
  5. 1 module MachineOutput;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_output.rb"; end
  8. 1 def followable?
  9. false
  10. end
  11. 1 def ok_to_read
  12. 2 left.ok_to_read
  13. end
  14. 1 def history?
  15. false
  16. end
  17. 1 event :remove_codename, :prepare_to_validate,
  18. on: :delete,
  19. when: proc { |c| c.codename.present? } do
  20. # load file before deleting codename otherwise it will fail later
  21. attachment
  22. self.codename = nil
  23. end
  24. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  25. 1 view :not_found do
  26. if update_machine_output_live?
  27. Card::Cache.reset_all # FIXME: wow, this is overkill, no?
  28. root.error_status = 302
  29. card.left.update_machine_output
  30. card_path card.left.machine_output_url
  31. else
  32. super()
  33. end
  34. end
  35. 1 def update_machine_output_live?
  36. case
  37. when !card.left.is_a?(Abstract::Machine) then false # must be a machine
  38. when card.left.locked? then false # machine must not be running
  39. when card.new_card? then true # always update if new
  40. else
  41. # must want current output (won't re-output old stuff)
  42. (selected_id = card.selected_action_id) &&
  43. selected_id == card.last_action_id
  44. end
  45. end
  46. end
  47. end;end;end;end;
  48. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/right/machine_output.rb ~~

card/tmpsets/set/mod007-card-mod-machines/self/script_decko.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptDecko"
  4. #
  5. 1 module ScriptDecko;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_decko.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 def source_files
  10. %w[mod editor name_editor autosave doubleclick layout navbox upload
  11. slot modal overlay recaptcha slotter bridge
  12. nest_editor nest_editor_rules nest_editor_options nest_editor_name
  13. link_editor
  14. components decko follow card_menu slot_ready
  15. filter filter_links filter_items selectable_filtered_content].map do |n|
  16. "decko/#{n}.js.coffee"
  17. end
  18. end
  19. end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_decko.rb ~~

card/tmpsets/set/mod007-card-mod-machines/self/script_html5shiv_printshiv.rb

100.0% lines covered

8 relevant lines. 8 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptHtml5shivPrintshiv"
  4. #
  5. 1 module ScriptHtml5shivPrintshiv;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_html5shiv_printshiv.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 view :script_tag, perms: :none do
  11. 224 <<-HTML.strip_heredoc
  12. <!--[if lt IE 9]>
  13. #{javascript_include_tag card.machine_output_url}
  14. <![endif]-->
  15. HTML
  16. end
  17. end
  18. end;end;end;end;
  19. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_html5shiv_printshiv.rb ~~

card/tmpsets/set/mod007-card-mod-machines/self/script_jquery.rb

77.78% lines covered

9 relevant lines. 7 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptJquery"
  4. #
  5. 1 module ScriptJquery;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_jquery.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 def source_files
  10. ["vendor/jquery_rails/vendor/assets/javascripts/jquery3.js",
  11. "vendor/jquery_rails/vendor/assets/javascripts/jquery_ujs.js"]
  12. end
  13. 1 def source_dir
  14. ""
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_jquery.rb ~~

card/tmpsets/set/mod007-card-mod-machines/self/script_jquery_helper.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptJqueryHelper"
  4. #
  5. 1 module ScriptJqueryHelper;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_jquery_helper.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptLibraries.add_item :script_jquery_helper
  10. 1 def source_files
  11. # jquery-ui includes all interaction components, the dialog and the autocomplete widget
  12. # and all dependencies for those
  13. # decko depends on autocomplete, sortable, jquery.autosize and jquery.fileupload
  14. # the dialog widget is not used in decko but in wikirate
  15. # don't know if iframe-transport is needed but it used to be there
  16. %w[jquery-ui.js
  17. jquery.autosize.js
  18. ../../vendor/jquery_file_upload/js/jquery.fileupload.js
  19. ../../vendor/jquery_file_upload/js/jquery.iframe-transport.js]
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/script_jquery_helper.rb ~~

card/tmpsets/set/mod007-card-mod-machines/self/style_bootstrap_compatible.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleBootstrapCompatible"
  4. #
  5. 1 module StyleBootstrapCompatible;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_bootstrap_compatible.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_bootstrap_compatible.rb ~~

card/tmpsets/set/mod007-card-mod-machines/self/style_cards.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleCards"
  4. #
  5. 1 module StyleCards;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_cards.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_cards.rb ~~

card/tmpsets/set/mod007-card-mod-machines/self/style_jquery_ui_smoothness.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleJqueryUiSmoothness"
  4. #
  5. 1 module StyleJqueryUiSmoothness;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_jquery_ui_smoothness.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/self/style_jquery_ui_smoothness.rb ~~

card/tmpsets/set/mod007-card-mod-machines/type/coffee_script.rb

64.71% lines covered

17 relevant lines. 11 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "CoffeeScript" cards
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module CoffeeScript;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/coffee_script.rb"; end
  9. 1 require "coffee-script"
  10. 1 include_set Abstract::Script
  11. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  12. 1 def ace_mode
  13. :coffee
  14. end
  15. end
  16. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  17. 1 view :core do
  18. compile_coffee _render_raw
  19. end
  20. 1 def compile_coffee script
  21. ::CoffeeScript.compile script
  22. rescue => e
  23. line_nr = e.to_s.match(/\[stdin\]:(\d*)/)&.capture(0)&.to_i
  24. line = script.lines[line_nr - 1] if line_nr
  25. raise Card::Error, "CoffeeScript::Error (#{card.name}): #{e.message}: #{line}"
  26. end
  27. end
  28. end;end;end;end;
  29. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/coffee_script.rb ~~

card/tmpsets/set/mod007-card-mod-machines/type/css.rb

59.52% lines covered

42 relevant lines. 25 lines covered and 17 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Css" cards
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module Css;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/css.rb"; end
  9. 1 require "sassc"
  10. 1 require "benchmark"
  11. 1 include_set Abstract::Machine
  12. 1 include_set Abstract::MachineInput
  13. 1 store_machine_output filetype: "css"
  14. 1 machine_input do
  15. compress_css format(format: :css)._render_core
  16. end
  17. 1 def compress_css input
  18. compress_css? ? SassC::Engine.new(input, style: :compressed).render : input
  19. rescue => e
  20. raise Card::Error, css_compression_error(e)
  21. end
  22. 1 def css_compression_error error
  23. # scss is compiled in a view
  24. # If there is a scss syntax error we get the rescued view here
  25. # and the error that the rescued view is no valid css
  26. # To get the original error we have to refer to Card::Error.current
  27. if Card::Error.current
  28. Card::Error.current.message
  29. else
  30. "Sass::SyntaxError (#{name}): #{error.message}"
  31. end
  32. end
  33. 1 def clean_html?
  34. false
  35. end
  36. 1 def compress_css?
  37. return true
  38. !Rails.env.development?
  39. end
  40. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  41. # def default_nest_view
  42. # :raw
  43. # end
  44. 1 def chunk_list # turn off autodetection of uri's
  45. :references
  46. end
  47. end
  48. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  49. 1 def input_type
  50. :ace_editor
  51. end
  52. 1 def ace_mode
  53. :css
  54. end
  55. 1 def default_nest_view
  56. :closed
  57. end
  58. 1 view :core do
  59. # FIXME: scan must happen before process for inclusion interactions to
  60. # work, but this will likely cause
  61. # problems with including other css?
  62. process_content ::CodeRay.scan(_render_raw, :css).div, size: :icon
  63. end
  64. 1 def content_changes action, diff_type, hide_diff=false
  65. wrap_with(:pre) { super }
  66. end
  67. end
  68. 2 module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
  69. 1 view :import do
  70. %{\n@import url("#{_render_url}");\n}
  71. end
  72. end
  73. 1 def diff_args
  74. { diff_format: :text }
  75. end
  76. end;end;end;end;
  77. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/css.rb ~~

card/tmpsets/set/mod007-card-mod-machines/type/java_script.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "JavaScript" cards
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module JavaScript;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/java_script.rb"; end
  9. 1 include_set Abstract::Script
  10. 2 module JsFormat; module_parent.send :register_set_format, Card::Format::JsFormat, self; extend Card::Set::AbstractFormat
  11. 1 view :core do
  12. _render_raw
  13. end
  14. end
  15. end;end;end;end;
  16. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/java_script.rb ~~

card/tmpsets/set/mod007-card-mod-machines/type/scss.rb

71.43% lines covered

14 relevant lines. 10 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Scss" cards
  4. #
  5. 1 module Scss;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/scss.rb"; end
  8. 1 include_set Type::Css
  9. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  10. 1 view :core, cache: :never do
  11. compile_scss(process_content(_render_raw))
  12. end
  13. 1 def compile_scss scss, style=:expanded
  14. SassC::Engine.new(scss, style: style).render
  15. rescue SassC::SyntaxError => e
  16. raise Card::Error,
  17. "SassC::SyntaxError (#{card.name}:#{e.sass_backtrace}): #{e.message}"
  18. # "#{#scss.lines[e.sass_line - 1]}\n" \
  19. end
  20. end
  21. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  22. 1 def ace_mode
  23. :scss
  24. end
  25. end
  26. end;end;end;end;
  27. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/scss.rb ~~

card/tmpsets/set/mod007-card-mod-machines/type/skin.rb

88.89% lines covered

9 relevant lines. 8 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Skin" cards
  4. #
  5. 1 module Skin;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-machines/set/type/skin.rb"; end
  8. 1 include_set Abstract::MachineInput
  9. 1 include_set Abstract::SkinBox
  10. 1 include_set Pointer
  11. 1 def machine_input
  12. # only the item of a skin card contribute input to the machine
  13. # not the skin card itself
  14. ""
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-machines/set/type/skin.rb ~~

card/tmpsets/set/mod008-settings/abstract/permission.rb

37.93% lines covered

58 relevant lines. 22 lines covered and 36 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Permission)
  4. #
  5. 1 module Permission;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/abstract/permission.rb"; end
  8. 1 def standardize_items
  9. 1 super unless content == "_left"
  10. end
  11. 1 def options_rule_card
  12. 303 Card[:cards_with_account]
  13. end
  14. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  15. 1 view :pointer_core do
  16. wrap_with :div, pointer_items, class: "pointer-list"
  17. end
  18. 1 view :core, cache: :never do
  19. if card.content == "_left"
  20. core_inherit_content
  21. else
  22. render! :pointer_core
  23. end
  24. end
  25. 1 view :one_line_content, cache: :never do
  26. render_core items: { view: :link }
  27. end
  28. 1 view :input do
  29. item_names = inheriting? ? [] : card.item_names
  30. %(
  31. #{_render_hidden_content_field}
  32. <div class="perm-editor">
  33. #{inheritance_checkbox}
  34. <div class="perm-group perm-vals perm-section">
  35. <h5 class="text-muted">Groups</h5>
  36. #{groups item_names}
  37. </div>
  38. <div class="perm-indiv perm-vals perm-section">
  39. <h5 class="text-muted">Individuals</h5>
  40. #{list_input item_list: item_names, extra_css_class: 'perm-indiv-ul'}
  41. </div>
  42. </div>
  43. )
  44. end
  45. 1 private
  46. 1 def groups item_names
  47. group_options.map do |option|
  48. checked = !item_names.delete(option.name).nil?
  49. icon = icon_tag "open_in_new", "link-muted"
  50. option_link = link_to_card option.name, icon, target: "decko_role"
  51. box = check_box_tag "#{option.key}-perm-checkbox",
  52. option.name, checked, class: "perm-checkbox-button"
  53. <<-HTML
  54. <div class="form-check checkbox">
  55. <label class="form-check-label">
  56. #{box} #{option.name} #{option_link}
  57. </label>
  58. </div>
  59. HTML
  60. end * "\n"
  61. end
  62. 1 def group_options
  63. Auth.as_bot do
  64. Card.search({ type_id: RoleID, sort: "name" }, "roles by name")
  65. end
  66. end
  67. 1 def inheritable?
  68. @inheritable ||=
  69. begin
  70. set_name = card.name.trunk_name
  71. set_card = Card.fetch(set_name)
  72. not_set = set_card && set_card.type_id != SetID
  73. not_set ? false : set_card.inheritable?
  74. end
  75. end
  76. 1 def inheriting?
  77. @inheriting ||= inheritable? && card.content == "_left"
  78. end
  79. 1 def inheritance_checkbox
  80. return unless inheritable?
  81. <<-HTML
  82. <div class="perm-inheritance perm-section">
  83. #{check_box_tag 'inherit', 'inherit', inheriting?}
  84. <label>
  85. #{core_inherit_content}
  86. #{wrap_with(:a, title: "use left's #{card.name.tag} rule") { '?' }}
  87. </label>
  88. </div>
  89. HTML
  90. end
  91. 1 def core_inherit_content
  92. text = if in_context_of_self_set?
  93. core_inherit_for_content_for_self_set
  94. else
  95. "Inherit from left card"
  96. end
  97. %(<span class="inherit-perm">#{text}</span>)
  98. end
  99. 1 def in_context_of_self_set?
  100. return false unless @set_context
  101. @set_context.to_name.tag_name.key == Card[:self].key
  102. end
  103. 1 def core_inherit_for_content_for_self_set
  104. task = card.tag.codename
  105. ancestor = Card[@set_context.trunk_name.trunk_name]
  106. links = ancestor.who_can(task).map do |card_id|
  107. link_to_card card_id, nil, target: args[:target]
  108. end * ", "
  109. "Inherit ( #{links} )"
  110. rescue
  111. "Inherit"
  112. end
  113. end
  114. end;end;end;end;
  115. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/abstract/permission.rb ~~

card/tmpsets/set/mod008-settings/abstract/templated_nests.rb

100.0% lines covered

8 relevant lines. 8 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (TemplatedNests)
  4. #
  5. 1 module TemplatedNests;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/abstract/templated_nests.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :core do
  10. 13 with_nest_mode :template do
  11. 13 super()
  12. end
  13. end
  14. end
  15. end;end;end;end;
  16. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/abstract/templated_nests.rb ~~

card/tmpsets/set/mod008-settings/all/supports_content_options.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (SupportsContentOptions)
  4. #
  5. 1 module SupportsContentOptions;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/all/supports_content_options.rb"; end
  8. 1 def supports_content_options?
  9. false
  10. end
  11. 1 def supports_content_option_view?
  12. false
  13. end
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/all/supports_content_options.rb ~~

card/tmpsets/set/mod008-settings/right/autoname.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Autoname" cards
  4. #
  5. 1 module Autoname;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/autoname.rb"; end
  8. 1 def history?
  9. false
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/autoname.rb ~~

card/tmpsets/set/mod008-settings/right/comment.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Comment" cards
  4. #
  5. 1 module Comment;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/comment.rb"; end
  8. 1 include_set Abstract::Permission
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/comment.rb ~~

card/tmpsets/set/mod008-settings/right/content_option_view.rb

66.67% lines covered

9 relevant lines. 6 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+ContentOptionView" cards
  4. #
  5. 1 module ContentOptionView;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/content_option_view.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def quick_edit
  10. if card.left.prototype_default_card&.try(:show_content_options?) &&
  11. card.left.prototype.rule_card(:input_type)&.supports_content_option_view?
  12. super
  13. else
  14. ""
  15. end
  16. end
  17. end
  18. end;end;end;end;
  19. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/content_option_view.rb ~~

card/tmpsets/set/mod008-settings/right/content_options.rb

61.54% lines covered

13 relevant lines. 8 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+ContentOptions" cards
  4. #
  5. 1 module ContentOptions;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/content_options.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def quick_edit
  10. card.left.prototype_default_card.try(:show_content_options?) ? super : ""
  11. end
  12. 1 def quick_editor
  13. wrap_type_formgroup do
  14. type_field class: "type-field rule-type-field _submit-on-select"
  15. end +
  16. wrap_content_formgroup do
  17. text_field :content, class: "d0-card-content _submit-after-typing"
  18. end
  19. end
  20. 1 def visible_cardtype_groups
  21. { "Organize" => %w[List Pointer] }
  22. end
  23. end
  24. end;end;end;end;
  25. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/content_options.rb ~~

card/tmpsets/set/mod008-settings/right/create.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Create" cards
  4. #
  5. 1 module Create;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/create.rb"; end
  8. 1 include_set Abstract::Permission
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/create.rb ~~

card/tmpsets/set/mod008-settings/right/default.rb

52.38% lines covered

21 relevant lines. 11 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Default" cards
  4. #
  5. 1 module Default;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/default.rb"; end
  8. 1 include_set Abstract::TemplatedNests
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 view :one_line_content do
  11. raw = _render_raw
  12. "#{card.type_name} : #{raw.present? ? raw : '<em>empty</em>'}"
  13. end
  14. 1 def quick_form_opts
  15. super.merge "data-update-foreign-slot":
  16. ".card-slot.quick_edit-view.RIGHT-Xinput_type,"\
  17. ".card-slot.quick_edit-view.RIGHT-Xcontent_option"\
  18. ".card-slot.quick_edit-view.RIGHT-Xcontent_option_view"
  19. end
  20. 1 def quick_editor
  21. wrap_type_formgroup do
  22. type_field class: "type-field rule-type-field _submit-on-select"
  23. end +
  24. wrap_content_formgroup do
  25. text_field :content, class: "d0-card-content _submit-after-typing"
  26. end
  27. end
  28. 1 def visible_cardtype_groups
  29. hash = ::Card::Set::Self::Cardtype::GROUP.slice("Text", "Data", "Upload")
  30. hash["Organize"] = ["List", "Pointer", "Link list", "Nest list"]
  31. hash
  32. end
  33. end
  34. 1 def empty_ok?
  35. true
  36. end
  37. end;end;end;end;
  38. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/default.rb ~~

card/tmpsets/set/mod008-settings/right/delete.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Delete" cards
  4. #
  5. 1 module Delete;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/delete.rb"; end
  8. 1 include_set Abstract::Permission
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/delete.rb ~~

card/tmpsets/set/mod008-settings/right/guide.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Guide" cards
  4. #
  5. # include_set Abstract::TemplatedNests
  6. 1 module Guide;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/guide.rb"; end
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 def raw_help_text
  11. # LOCALIZE
  12. "Appears in the full editor view to guide users."
  13. end
  14. end
  15. end;end;end;end;
  16. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/guide.rb ~~

card/tmpsets/set/mod008-settings/right/help.rb

72.73% lines covered

11 relevant lines. 8 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Help" cards
  4. #
  5. 1 module Help;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/help.rb"; end
  8. 1 include_set Abstract::TemplatedNests
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 view :popover do
  11. popover_link _render_core
  12. end
  13. 1 def quick_editor
  14. # TODO: refactor when voo.input_type is ready. (and use class_up)
  15. formgroup "Content", input: :content, help: false do
  16. text_field :content, value: card.content,
  17. class: "d0-card-content _submit-after-typing"
  18. end
  19. end
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/help.rb ~~

card/tmpsets/set/mod008-settings/right/input_type.rb

65.0% lines covered

20 relevant lines. 13 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+InputType" cards
  4. #
  5. 1 module InputType;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/input_type.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def quick_editor
  10. @submit_on_change = true
  11. super
  12. end
  13. 1 def quick_form_opts
  14. super.merge "data-update-foreign-slot":
  15. ".card-slot.quick_edit-view.RIGHT-Xcontent_option_view"
  16. end
  17. 1 def default_input_type
  18. :radio
  19. end
  20. 1 def raw_help_text
  21. 6 "edit interface for list cards"
  22. end
  23. # def option_label_text option_name
  24. # super.downcase
  25. # end
  26. 1 def quick_edit
  27. card.left.prototype_default_card.try(:show_input_type?) ? super : ""
  28. end
  29. end
  30. 1 def option_names
  31. left.prototype_default_card&.try(:input_type_content_options) || super
  32. end
  33. 1 def supports_content_option_view?
  34. item_name.in? ["checkbox", "radio", "filtered list"]
  35. end
  36. end;end;end;end;
  37. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/input_type.rb ~~

card/tmpsets/set/mod008-settings/right/read.rb

40.91% lines covered

44 relevant lines. 18 lines covered and 26 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Read" cards
  4. #
  5. 1 module Read;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/read.rb"; end
  8. 1 include Abstract::Permission
  9. 2 format :html do include Abstract::Permission::HtmlFormat end
  10. 1 event :cascade_read_rule, :finalize, after: :update_rule_cache, when: :is_rule? do
  11. return unless name_is_changing? || trash_is_changing?
  12. update_read_ruled_cards
  13. end
  14. 1 def update_read_ruled_cards
  15. Card::Rule.clear_read_rule_cache
  16. Card.cache.reset # maybe be more surgical, just Auth.user related
  17. expire # probably shouldn't be necessary,
  18. # but was sometimes getting cached version when card should be in the
  19. # trash. could be related to other bugs?
  20. processed = update_read_rules_of_set_members
  21. update_cards_with_read_rule_id processed unless new?
  22. end
  23. 1 def update_read_rules_of_set_members
  24. return unless rule_pattern_index
  25. each_member do |member, processed|
  26. processed << member.key
  27. member.update_read_rule unless member_has_overriding_rule?(member)
  28. end
  29. end
  30. 1 def member_has_overriding_rule? member
  31. pattern_index(Card.fetch_id(member.read_rule_class)) < rule_pattern_index
  32. end
  33. # cards with this card as a read_rule_id
  34. # These may include cards that are no longer set members if the card was renamed
  35. # (edge case)
  36. 1 def update_cards_with_read_rule_id processed
  37. processed ||= ::Set.new
  38. Card::Auth.as_bot do
  39. Card.search(read_rule_id: id) do |card|
  40. card.update_read_rule unless processed.include?(card.key)
  41. end
  42. end
  43. end
  44. 1 def each_member
  45. Auth.as_bot do
  46. all_members.each_with_object(::Set.new) do |member, processed|
  47. yield member, processed
  48. end
  49. end
  50. end
  51. 1 def all_members
  52. rule_set.item_cards limit: 0
  53. end
  54. 1 def rule_pattern_index
  55. return if trash
  56. @rule_pattern_index ||= pattern_index rule_set&.tag&.id
  57. end
  58. 1 def pattern_index pattern_id
  59. pattern_ids.index(pattern_id) || invalid_pattern_id(pattern_id)
  60. end
  61. 1 def pattern_ids
  62. @pattern_ids ||= set_patterns.map(&:pattern_id)
  63. end
  64. 1 def invalid_pattern_id pattern_id
  65. Rails.logger.info "invalid pattern id for read rule: #{pattern_id}"
  66. end
  67. 1 event :process_read_rule_update_queue, :finalize do
  68. left&.update_read_rule
  69. end
  70. end;end;end;end;
  71. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/read.rb ~~

card/tmpsets/set/mod008-settings/right/script.rb

84.62% lines covered

13 relevant lines. 11 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Script" cards
  4. #
  5. 1 module Script;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/script.rb"; end
  8. 1 include_set Abstract::Machine
  9. 1 store_machine_output filetype: "js"
  10. 1 def ok_to_read
  11. 1 true
  12. end
  13. 1 view :javascript_include_tag do
  14. %(
  15. <script src="#{card.machine_output_url}" type="text/javascript"></script>
  16. )
  17. end
  18. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  19. 1 def raw_help_text
  20. "JavaScript (or CoffeeScript) for card's page."
  21. end
  22. end
  23. end;end;end;end;
  24. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/script.rb ~~

card/tmpsets/set/mod008-settings/right/structure.rb

75.0% lines covered

40 relevant lines. 30 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Structure" cards
  4. #
  5. 1 module Structure;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/structure.rb"; end
  8. 1 include_set Abstract::TemplatedNests
  9. 2 module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
  10. 1 def raw_feed_items
  11. [card]
  12. end
  13. end
  14. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  15. 1 view :one_line_content do
  16. "#{_render_type} : #{_render_raw}"
  17. end
  18. 1 def visible_cardtype_groups
  19. hash = ::Card::Set::Self::Cardtype::GROUP.slice("Text")
  20. hash["Organize"] = ["Search", "Nest list"]
  21. hash
  22. end
  23. end
  24. 1 event :update_structurees_references, :integrate,
  25. when: :update_structurees_references? do
  26. 8 return unless (query = structuree_query)
  27. 8 Auth.as_bot do
  28. 8 query.run.each(&:update_references_out)
  29. end
  30. end
  31. 1 def update_structurees_references?
  32. 8 db_content_changed? || action == :delete
  33. end
  34. 1 event :reset_cache_to_use_new_structure,
  35. before: :update_structurees_references do
  36. 8 Card::Cache.reset_hard
  37. 8 Card::Cache.reset_soft
  38. end
  39. 1 event :update_structurees_type, :finalize,
  40. 8 changed: :type_id, when: proc { |c| c.assigns_type? } do
  41. update_structurees type_id: type_id
  42. end
  43. 1 def structuree_names
  44. 8 return [] unless (query = structuree_query(return: :name))
  45. 8 Auth.as_bot do
  46. 8 query.run
  47. end
  48. end
  49. 1 def update_structurees args
  50. # note that this is not smart about overriding templating rules
  51. # for example, if someone were to change the type of a
  52. # +*right+*structure rule that was overridden
  53. # by a +*type plus right+*structure rule, the override would not be respected.
  54. return unless (query = structuree_query(return: :id))
  55. Auth.as_bot do
  56. query.run.each_slice(100) do |id_batch|
  57. Card.where(id: id_batch).update_all args
  58. end
  59. end
  60. end
  61. 1 def structuree_query args={}
  62. 16 set_card = trunk
  63. 16 return unless set_card.type_id == SetID
  64. 16 set_card.fetch_query args
  65. end
  66. end;end;end;end;
  67. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/structure.rb ~~

card/tmpsets/set/mod008-settings/right/style.rb

58.82% lines covered

34 relevant lines. 20 lines covered and 14 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Style" cards
  4. #
  5. 1 module Style;
  6. 1 extend Card::Set
  7. 2 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/style.rb"; end
  8. 1 require "sassc"
  9. 1 include_set Abstract::Machine
  10. 1 store_machine_output filetype: "css"
  11. 1 def ok_to_read
  12. 225 true
  13. end
  14. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  15. # turn off autodetection of uri's
  16. 1 def chunk_list
  17. :nest_only
  18. end
  19. end
  20. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  21. 1 HIDDEN_SKINS = %w[bootstrap_default_skin themeless_bootstrap_skin bootstrap_default_skin
  22. classic_bootstrap_skin].freeze
  23. 1 def default_item_view
  24. :bar
  25. end
  26. 1 view :input, template: :haml
  27. 1 def themes
  28. card.rule_card(:content_options).item_cards
  29. end
  30. 1 def selectable_themes
  31. themes.reject do |theme_card|
  32. theme_card.right&.codename == :stylesheets ||
  33. theme_card.key.in?(HIDDEN_SKINS)
  34. end
  35. end
  36. end
  37. 1 event :customize_theme, :prepare_to_validate, on: :update, when: :customize_theme? do
  38. skin_name = free_skin_name
  39. add_subcard skin_name, type_id: CustomizedBootswatchSkinID
  40. self.content = "[[#{skin_name}]]"
  41. end
  42. 1 def free_skin_name
  43. name = "#{@theme} skin customized"
  44. if Card.exist?(name)
  45. name = "#{name} 1"
  46. name.next! while Card.exist?(name)
  47. end
  48. name
  49. end
  50. 1 def customize_theme?
  51. Env.params[:customize].present? && (@theme = Env.params[:theme]).present?
  52. end
  53. end;end;end;end;
  54. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/style.rb ~~

card/tmpsets/set/mod008-settings/right/update.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Update" cards
  4. #
  5. 1 module Update;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/right/update.rb"; end
  8. 1 include_set Abstract::Permission
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/right/update.rb ~~

card/tmpsets/set/mod008-settings/self/autoname.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Autoname"
  4. #
  5. 1 module Autoname;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/autoname.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :templating, position: 4,
  10. help_text: "Autogenerate name for new cards by incrementing this value. "\
  11. "[[http://decko.org/autonaming|more]]"
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/autoname.rb ~~

card/tmpsets/set/mod008-settings/self/captcha.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Captcha"
  4. #
  5. 1 module Captcha;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/captcha.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :permission, position: 5,
  10. help_text: "Anti-spam setting. Requires non-signed-in users to complete a "\
  11. "[[http://decko.org/captcha|captcha]] before adding or editing "\
  12. "cards (where permitted)."
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/captcha.rb ~~

card/tmpsets/set/mod008-settings/self/content_option_view.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ContentOptionView"
  4. #
  5. 1 module ContentOptionView;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/content_option_view.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :editing, position: 6,
  10. restricted_to_type: %i[list pointer session],
  11. rule_type_editable: false,
  12. help_text: "Label view for radio button and checkbox items. "\
  13. "[[http://decko.org/Pointer|more]]",
  14. applies: lambda { |prototype|
  15. prototype.supports_content_options? &&
  16. prototype.rule_card(:input_type)&.supports_content_option_view?
  17. }
  18. end;end;end;end;
  19. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/content_option_view.rb ~~

card/tmpsets/set/mod008-settings/self/content_options.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ContentOptions"
  4. #
  5. 1 module ContentOptions;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/content_options.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :editing, position: 5,
  10. restricted_to_type: %i[list pointer session],
  11. rule_type_editable: true,
  12. help_text: "Value options for [[List]] and [[Pointer]] and cards. "\
  13. "Can itself be a List or a [[Search]]. "\
  14. "[[http://decko.org/Pointer|more]]",
  15. applies: lambda { |prototype|
  16. prototype.rule_card(:input_type).supports_content_options?
  17. }
  18. end;end;end;end;
  19. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/content_options.rb ~~

card/tmpsets/set/mod008-settings/self/create.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Create"
  4. #
  5. 1 module Create;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/create.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :permission, position: 1, rule_type_editable: false,
  10. short_help_text: "who can create cards",
  11. help_text: "Who can create new cards"
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/create.rb ~~

card/tmpsets/set/mod008-settings/self/csv_structure.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "CsvStructure"
  4. #
  5. 1 module CsvStructure;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/csv_structure.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :templating, position: 3, rule_type_editable: true
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/csv_structure.rb ~~

card/tmpsets/set/mod008-settings/self/default.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Default"
  4. #
  5. 1 module Default;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/default.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :templating, position: 2, rule_type_editable: true,
  10. short_help_text: "type/content template for new cards"
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/default.rb ~~

card/tmpsets/set/mod008-settings/self/default_html_view.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "DefaultHtmlView"
  4. #
  5. 1 module DefaultHtmlView;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/default_html_view.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :other, position: 4, rule_type_editable: false
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/default_html_view.rb ~~

card/tmpsets/set/mod008-settings/self/delete.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Delete"
  4. #
  5. 1 module Delete;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/delete.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :permission, position: 4, rule_type_editable: false,
  10. short_help_text: "who can delete cards",
  11. help_text: "Who can delete cards"
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/delete.rb ~~

card/tmpsets/set/mod008-settings/self/follow_fields.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "FollowFields"
  4. #
  5. 1 module FollowFields;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/follow_fields.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :other, position: 5, rule_type_editable: false
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/follow_fields.rb ~~

card/tmpsets/set/mod008-settings/self/guide.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Guide"
  4. #
  5. 1 module Guide;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/guide.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :editing, position: 3, rule_type_editable: true,
  10. short_help_text: "appears in the full editor view to guide users"
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/guide.rb ~~

card/tmpsets/set/mod008-settings/self/head.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Head"
  4. #
  5. 1 module Head;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/head.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :webpage, position: 1, rule_type_editable: false,
  10. short_help_text: "head tag content",
  11. help_text: "head tag content"
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/head.rb ~~

card/tmpsets/set/mod008-settings/self/help.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Help"
  4. #
  5. 1 module Help;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/help.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :editing, position: 1, rule_type_editable: true,
  10. short_help_text: "help text people will see when editing",
  11. help_text: "[[http://decko.org/custom_help_text|Help text]] "\
  12. "people will see when editing."
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/help.rb ~~

card/tmpsets/set/mod008-settings/self/input_type.rb

88.89% lines covered

9 relevant lines. 8 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "InputType"
  4. #
  5. 1 module InputType;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/input_type.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :editing,
  10. position: 3,
  11. rule_type_editable: false,
  12. short_help_text: "edit interface"
  13. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  14. 1 def raw_help_text
  15. "Configure [[https://ace.c9.io/|ace]], "\
  16. "Decko's default code editor, using these available "\
  17. "[[https://github.com/ajaxorg/ace/wiki/Configuring-Ace|options]]."
  18. end
  19. end
  20. end;end;end;end;
  21. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/input_type.rb ~~

card/tmpsets/set/mod008-settings/self/layout.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Layout"
  4. #
  5. 1 module Layout;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/layout.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :webpage, position: 3, rule_type_editable: false,
  10. help_text: "HTML structure of card's page "\
  11. "[[http://decko.org/layouts | more]]"
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/layout.rb ~~

card/tmpsets/set/mod008-settings/self/on_create.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "OnCreate"
  4. #
  5. 1 module OnCreate;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_create.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :event, position: 1, rule_type_editable: false,
  10. help_text: "Configure events to be executed when card is created"
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_create.rb ~~

card/tmpsets/set/mod008-settings/self/on_delete.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "OnDelete"
  4. #
  5. 1 module OnDelete;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_delete.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :event, position: 3, rule_type_editable: false,
  10. help_text: "Configure events to be executed when card is deleted"
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_delete.rb ~~

card/tmpsets/set/mod008-settings/self/on_update.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "OnUpdate"
  4. #
  5. 1 module OnUpdate;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_update.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :event, position: 2, rule_type_editable: false,
  10. help_text: "Configure events to be executed when card is updated"
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/on_update.rb ~~

card/tmpsets/set/mod008-settings/self/read.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Read"
  4. #
  5. 1 module Read;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/read.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :permission, position: 2, rule_type_editable: false,
  10. short_help_text: "who can view cards",
  11. help_text: "Who can view cards in the [[set]]."
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/read.rb ~~

card/tmpsets/set/mod008-settings/self/recent_settings.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "RecentSettings"
  4. #
  5. 1 module RecentSettings;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/recent_settings.rb"; end
  8. 1 def history?
  9. 111 false
  10. end
  11. 1 def followable?
  12. false
  13. end
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/recent_settings.rb ~~

card/tmpsets/set/mod008-settings/self/script.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Script"
  4. #
  5. 1 module Script;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/script.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :webpage, position: 5
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/script.rb ~~

card/tmpsets/set/mod008-settings/self/structure.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Structure"
  4. #
  5. 1 module Structure;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/structure.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :templating, position: 2, rule_type_editable: true,
  10. short_help_text: "control card's content / structure",
  11. help_text: "Controls cards' content / structure. "\
  12. "[[http://decko.org/formatting|more]]"
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/structure.rb ~~

card/tmpsets/set/mod008-settings/self/style.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Style"
  4. #
  5. 1 module Style;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/style.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :webpage, position: 4,
  10. help_text: "Skin (collection of stylesheets) for card's page. "\
  11. "[[http://decko.org/skins|more]]"
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/style.rb ~~

card/tmpsets/set/mod008-settings/self/table_of_contents.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "TableOfContents"
  4. #
  5. 1 module TableOfContents;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/table_of_contents.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :other, position: 1, rule_type_editable: false,
  10. help_text: "Autogenerate [[http://decko.org/table_of_contents|"\
  11. "table of contents]] on cards with at least this many headers "\
  12. '("0" means never).'
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/table_of_contents.rb ~~

card/tmpsets/set/mod008-settings/self/thanks.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Thanks"
  4. #
  5. 1 module Thanks;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/thanks.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :other, position: 3, rule_type_editable: false,
  10. short_help_text: "destination after card is created",
  11. help_text: "Destination after card is created. "\
  12. "[[http://decko.org/Custom_thank_you_messages_for_forms|more]]"
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/thanks.rb ~~

card/tmpsets/set/mod008-settings/self/update.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Update"
  4. #
  5. 1 module Update;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/self/update.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :permission, position: 3, rule_type_editable: false,
  10. short_help_text: "who can update cards",
  11. help_text: "Who can update cards"
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/self/update.rb ~~

card/tmpsets/set/mod008-settings/type/setting.rb

51.52% lines covered

33 relevant lines. 17 lines covered and 16 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Setting" cards
  4. #
  5. # require "json"
  6. 1 module Setting;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/settings/set/type/setting.rb"; end
  9. 1 def self.member_names
  10. @@member_names ||= begin
  11. Card.search(
  12. { type_id: SettingID, return: "key" },
  13. "all setting cards"
  14. ).each_with_object({}) do |card_key, hash|
  15. hash[card_key] = true
  16. end
  17. end
  18. end
  19. 2 module DataFormat; module_parent.send :register_set_format, Card::Format::DataFormat, self; extend Card::Set::AbstractFormat
  20. 1 view :core do
  21. cql = { left: { type: SetID },
  22. right: card.id,
  23. limit: 0 }
  24. Card.search(cql).compact.map { |c| nest c }
  25. end
  26. end
  27. 1 def count
  28. 19 Card.search left: { type: SetID }, right: id, limit: 0, return: :count
  29. end
  30. 1 def set_classes_with_rules
  31. Card.set_patterns.reverse.map do |set_class|
  32. cql = { left: { type: SetID },
  33. right: id,
  34. sort: %w[content name],
  35. limit: 0 }
  36. cql[:left][(set_class.anchorless? ? :id : :right_id)] = set_class.pattern_id
  37. rules = Card.search cql
  38. [set_class, rules] unless rules.empty?
  39. end.compact
  40. end
  41. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  42. 1 def rule_link rule, text
  43. link_to_card rule, text, path: { view: :modal_rule },
  44. slotter: true, "data-modal-class": "modal-lg"
  45. end
  46. 1 view :core do
  47. haml do
  48. <<-'HAML'.strip_heredoc
  49. = _render_rule_help
  50. %h3 All #{card.name.tr "*", ""} rules that apply to
  51. - card.set_classes_with_rules.each do |klass, rules|
  52. %p
  53. %h5
  54. = klass.generic_label.downcase
  55. - if klass.anchorless?
  56. = nest rules.first, view: :bar, show: :full_name
  57. - else
  58. - rules.each do |rule|
  59. = nest rule, view: :bar
  60. HAML
  61. end
  62. end
  63. # Because +*help content renders in "template" mode when you render its content
  64. # directly, we render the help text in the context of the *all+<setting> card
  65. 1 view :rule_help do
  66. nest [:all, card.name], view: :rule_help
  67. end
  68. 1 view :one_line_content do
  69. render_rule_help
  70. end
  71. end
  72. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  73. 1 def items_for_export
  74. Card.search left: { type: SetID }, right: card.id, limit: 0
  75. end
  76. end
  77. end;end;end;end;
  78. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/settings/set/type/setting.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/00_filter_helper.rb

83.33% lines covered

30 relevant lines. 25 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (FilterHelper)
  4. #
  5. # TODO: move sort/filter handling out of card and into base format
  6. 1 module FilterHelper;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/00_filter_helper.rb"; end
  9. # sorting and filtering is about viewing the data, not altering the data itself.
  10. 1 def sort_hash
  11. 266 sort_param.present? ? { sort: sort_param } : {}
  12. end
  13. 1 def filter_param field
  14. filter_hash[field.to_sym]
  15. end
  16. # FIXME: it is inconsistent that #sort_hash has :sort as the key, but
  17. # #filter_hash is the _value_ of the hash with :filter as the key.
  18. 1 def filter_hash
  19. 266 @filter_hash ||=
  20. 247 Env.params[:filter].present? ? Env.hash(Env.params[:filter]) : default_filter_hash
  21. end
  22. 1 def sort_param
  23. 266 @sort_param ||= safe_sql_param :sort
  24. end
  25. 1 def safe_sql_param key
  26. 266 param = Env.params[key]
  27. 266 param.blank? ? nil : Card::Query.safe_sql(param)
  28. end
  29. 1 def filter_keys_with_values
  30. filter_keys.map do |key|
  31. values = filter_param(key)
  32. values.present? ? [key, values] : next
  33. end.compact
  34. end
  35. # initial values for filtered search
  36. 1 def default_filter_hash
  37. 247 {}
  38. end
  39. 1 def offset
  40. param_to_i :offset, 0
  41. end
  42. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  43. 1 delegate :filter_hash, :sort_hash, :filter_param, :sort_param,
  44. :all_filter_keys, to: :card
  45. 1 def extra_paging_path_args
  46. 19 super.merge filter_and_sort_hash
  47. end
  48. 1 def filter_and_sort_hash
  49. 19 sort_hash.merge filter: filter_hash
  50. end
  51. end
  52. end;end;end;end;
  53. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/00_filter_helper.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/02_search_params.rb

90.91% lines covered

33 relevant lines. 30 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (SearchParams)
  4. #
  5. 1 module SearchParams;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/02_search_params.rb"; end
  8. 1 include_set Abstract::PagingParams
  9. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  10. 1 def offset
  11. 29 search_params[:offset] || 0
  12. end
  13. 1 def search_params
  14. 72 @search_params ||= default_search_params
  15. end
  16. # used for override
  17. 1 def default_search_params
  18. 11 if (qparams = query_params)
  19. 4 paging_params.merge vars: qparams
  20. else
  21. 7 paging_params
  22. end
  23. end
  24. 1 def paging_params
  25. 11 { limit: limit_param, offset: offset_param }
  26. end
  27. 1 def query_params
  28. 36 return nil unless (vars = params[:query])
  29. 29 Env.hash vars
  30. end
  31. 1 def default_limit
  32. 100
  33. end
  34. 1 def extra_paging_path_args
  35. 19 return {} unless (vars = query_params)
  36. 19 { query: vars }
  37. end
  38. end
  39. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  40. 1 def default_limit
  41. 10 Cardio.config.paging_limit || 20
  42. end
  43. end
  44. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  45. 1 def default_limit
  46. 20
  47. end
  48. end
  49. 2 module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
  50. 1 def default_limit
  51. 25
  52. end
  53. end
  54. end;end;end;end;
  55. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/02_search_params.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/03_filter.rb

75.0% lines covered

16 relevant lines. 12 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Filter)
  4. #
  5. 1 module Filter;
  6. 1 extend Card::Set
  7. 3 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter.rb"; end
  8. 1 include_set Abstract::Utility
  9. 1 def filter_class
  10. Card::FilterQuery
  11. end
  12. 1 def filter_keys
  13. [:name]
  14. end
  15. 1 def filter_keys_from_params
  16. filter_hash.keys.map(&:to_sym) - [:not_ids]
  17. end
  18. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  19. 1 def sort_options
  20. { "Alphabetical": :name, "Recently Added": :create }
  21. end
  22. 1 view :filtered_content, template: :haml, wrap: :slot
  23. 1 view :selectable_filtered_content, template: :haml, cache: :never
  24. end
  25. end;end;end;end;
  26. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/03_filter/filter_form.rb

44.44% lines covered

45 relevant lines. 20 lines covered and 25 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Filter;
  3. # Set: Abstract (Filter, FilterForm)
  4. #
  5. 1 module FilterForm;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/filter_form.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # sort and filter ui
  10. 1 view :filter_form, cache: :never do
  11. filter_fields slot_selector: "._filter-result-slot",
  12. sort_field: _render(:sort_formgroup)
  13. end
  14. 1 view :quick_filters, cache: :never do
  15. return "" unless quick_filter_list.present?
  16. haml :quick_filters, filter_list: normalized_quick_filter_list
  17. end
  18. 1 def normalized_quick_filter_list
  19. quick_filter_list.map do |hash|
  20. hash = hash.clone
  21. filter_key = hash.keys.first
  22. {
  23. text: (hash.delete(:text) || hash[filter_key]),
  24. icon: (hash.delete(:icon) || mapped_icon_tag(filter_key)),
  25. # FIXME: mapped_icon_tag is a wikirate concept
  26. class: css_classes(hash.delete(:class),
  27. "_filter-link quick-filter-by-#{filter_key}"),
  28. filter: JSON(hash[:filter] || hash)
  29. }
  30. end
  31. end
  32. # for override
  33. 1 def quick_filter_list
  34. []
  35. end
  36. # for override
  37. 1 def custom_quick_filters
  38. ""
  39. end
  40. # @param data [Hash] the filter categories. The hash needs for every category
  41. # a hash with a label and a input_field entry.
  42. 1 def filter_form data={}, sort_input_field=nil, form_args={}
  43. haml :filter_form, categories: data,
  44. sort_input_field: sort_input_field,
  45. form_args: form_args
  46. end
  47. 1 def filter_fields slot_selector: nil, sort_field: nil
  48. form_args = { action: filter_action_path, class: "slotter" }
  49. form_args["data-slot-selector"] = slot_selector if slot_selector
  50. filter_form filter_form_data, sort_field, form_args
  51. end
  52. 1 def filter_form_data
  53. all_filter_keys.each_with_object({}) do |cat, h|
  54. h[cat] = { label: filter_label(cat),
  55. input_field: _render("filter_#{cat}_formgroup"),
  56. active: active_filter?(cat),
  57. default: default_filter?(cat) }
  58. end
  59. end
  60. 1 def active_filter? field
  61. if card.filter_keys_from_params.present?
  62. filter_hash.key? field
  63. else
  64. default_filter? field
  65. end
  66. end
  67. 1 def default_filter? field
  68. card.default_filter_hash.key? field
  69. end
  70. 1 def filter_label field
  71. # return "Keyword" if field.to_sym == :name
  72. #
  73. filter_label_from_method(field) || filter_label_from_name(field)
  74. end
  75. 1 def filter_label_from_method field
  76. try "#{field}_filter_label"
  77. end
  78. 1 def filter_label_from_name field
  79. Card.fetch_name(field) { field.to_s.titleize }
  80. end
  81. 1 def filter_action_path
  82. path
  83. end
  84. 1 view :sort_formgroup, cache: :never do
  85. select_tag "sort",
  86. options_for_select(sort_options, card.current_sort),
  87. class: "pointer-select _filter-sort form-control",
  88. "data-minimum-results-for-search": "Infinity"
  89. end
  90. end
  91. end;end;end;end;end;
  92. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/filter_form.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/03_filter/form_helper.rb

35.94% lines covered

64 relevant lines. 23 lines covered and 41 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Filter;
  3. # Set: Abstract (Filter, FormHelper)
  4. #
  5. 1 module FormHelper;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/form_helper.rb"; end
  8. 1 include_set Abstract::FilterHelper
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 view :filter_name_formgroup, cache: :never do
  11. text_filter :name
  12. end
  13. 1 def select_filter field, default=nil, options=nil
  14. options ||= filter_options field
  15. options.unshift(["--", ""]) unless default
  16. select_filter_tag field, default, options
  17. end
  18. 1 def multiselect_filter field, default=nil, options=nil
  19. options ||= filter_options field
  20. multiselect_filter_tag field, default, options
  21. end
  22. 1 def text_filter field, default=nil, opts={}
  23. value = filter_param(field) || default
  24. text_filter_with_name_and_value filter_name(field), value, opts
  25. end
  26. 1 def text_filter_with_name_and_value name, value, opts
  27. opts[:class] ||= "simple-text"
  28. add_class opts, "form-control"
  29. text_field_tag name, value, opts
  30. end
  31. 1 def range_filter field, default={}, opts={}
  32. add_class opts, "simple-text range-filter-subfield"
  33. default ||= {}
  34. output [range_sign(:from),
  35. sub_text_filter(field, :from, default, opts),
  36. range_sign(:to),
  37. sub_text_filter(field, :to, default, opts)]
  38. end
  39. 1 def range_sign side
  40. dir = side == :from ? "right" : "left"
  41. wrap_with :span, class: "input-group-prepend" do
  42. fa_icon("chevron-circle-#{dir}", class: "input-group-text")
  43. end
  44. end
  45. 1 def sub_text_filter field, subfield, default={}, opts={}
  46. name = "filter[#{field}][#{subfield}]"
  47. value = filter_hash.dig(field, subfield) || default[subfield]
  48. text_filter_with_name_and_value name, value, opts
  49. end
  50. 1 def autocomplete_filter type_code, options_card=nil
  51. options_card ||= Card::Name[type_code, :type, :by_name]
  52. text_filter type_code, "", class: "#{type_code}_autocomplete",
  53. "data-options-card": options_card
  54. end
  55. 1 def multiselect_filter_tag field, default, options, html_options={}
  56. html_options[:multiple] = true
  57. select_filter_tag field, default, options, html_options
  58. end
  59. 1 def select_filter_tag field, default, options, html_options={}
  60. name = filter_name field, html_options[:multiple]
  61. options = options_for_select options, (filter_param(field) || default)
  62. normalize_select_filter_tag_html_options field, html_options
  63. select_tag name, options, html_options
  64. end
  65. # alters html_options hash
  66. 1 def normalize_select_filter_tag_html_options field, html_options
  67. pointer_suffix = html_options[:multiple] ? "multiselect" : "select"
  68. add_class html_options, "pointer-#{pointer_suffix} filter-input #{field} " \
  69. "_filter_input_field _no-select2 form-control"
  70. # _no-select2 because select is initiated after filter is opened.
  71. html_options[:id] = "filter-input-#{unique_id}"
  72. end
  73. 1 def filter_name field, multi=false
  74. "filter[#{field}]#{'[]' if multi}"
  75. end
  76. 1 def filter_options field
  77. raw = send("#{field}_options")
  78. raw.is_a?(Array) ? raw : option_hash_to_array(raw)
  79. end
  80. 1 def option_hash_to_array hash
  81. hash.each_with_object([]) do |(key, value), array|
  82. array << [key, value.to_s]
  83. array
  84. end
  85. end
  86. 1 def type_options type_codename, order="asc", max_length=nil
  87. Card.cache.fetch "#{type_codename}-TYPE-OPTIONS" do
  88. res = Card.search type: type_codename, return: :name, sort: "name", dir: order
  89. max_length ? (res.map { |i| [trim_option(i, max_length), i] }) : res
  90. end
  91. end
  92. 1 def trim_option option, max_length
  93. option.size > max_length ? "#{option[0..max_length]}..." : option
  94. end
  95. end
  96. end;end;end;end;end;
  97. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/form_helper.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/03_filter/query_construction.rb

68.18% lines covered

22 relevant lines. 15 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Filter;
  3. # Set: Abstract (Filter, QueryConstruction)
  4. #
  5. # all filter keys in the order they were selected
  6. 1 module QueryConstruction;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/query_construction.rb"; end
  9. 1 def all_filter_keys
  10. @all_filter_keys ||= filter_keys_from_params | filter_keys
  11. end
  12. 1 def filter_and_sort_cql
  13. 247 filter_cql.merge(sort_cql)
  14. end
  15. 1 def filter_cql
  16. 247 return {} if filter_hash.empty?
  17. filter_cql_from_params
  18. end
  19. # separate method is needed for tests
  20. 1 def filter_cql_from_params
  21. filter_class.new(filter_keys_with_values, blocked_id_cql).to_cql
  22. end
  23. 1 def sort_cql
  24. 247 sort_hash
  25. end
  26. 1 def blocked_id_cql
  27. not_ids = filter_param :not_ids
  28. not_ids.present? ? { id: ["not in", not_ids.split(",")] } : {}
  29. end
  30. 1 def current_sort
  31. sort_param || default_sort_option
  32. end
  33. 1 def default_sort_option
  34. cql_content[:sort]
  35. end
  36. end;end;end;end;end;
  37. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/03_filter/query_construction.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/04_right_filter_form.rb

68.75% lines covered

16 relevant lines. 11 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (RightFilterForm)
  4. #
  5. # To be included in a field card to get a filter for the parent.
  6. 1 module RightFilterForm;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/04_right_filter_form.rb"; end
  9. # The core view renders a filter for the left card.
  10. 1 include_set Set::Abstract::Filter
  11. 1 def virtual?
  12. new?
  13. end
  14. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  15. 1 def filter_action_path
  16. path mark: card.name.left, view: filter_view
  17. end
  18. 1 view :core, cache: :never do
  19. filter_fields slot_selector: filter_selector
  20. end
  21. 1 def filter_view
  22. :filter_result
  23. end
  24. 1 def filter_selector
  25. ".#{filter_view}-view"
  26. end
  27. end
  28. end;end;end;end;
  29. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/04_right_filter_form.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/05_search.rb

76.47% lines covered

51 relevant lines. 39 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Search)
  4. #
  5. 1 module Search;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/05_search.rb"; end
  8. 1 include_set Abstract::Paging
  9. 1 include_set Abstract::SearchParams
  10. 1 include_set Abstract::Filter
  11. 1 def search _args={}
  12. raise Error, "search not overridden"
  13. end
  14. 1 def cached_search args={}
  15. 35 @search_results ||= {}
  16. 35 @search_results[args.to_s] ||= search args
  17. end
  18. 1 def returning item, args
  19. 6 args[:return] = item
  20. 6 yield
  21. end
  22. 1 def item_cards args={}
  23. args[:limit] ||= 0
  24. returning(:card, args) { search args }
  25. end
  26. 1 def item_names args={}
  27. 4 args[:limit] ||= 0
  28. 8 returning(:name, args) { search args }
  29. end
  30. 1 def item_ids args={}
  31. args[:limit] ||= 0
  32. returning(:id, args) { search args }
  33. end
  34. 1 def count args={}
  35. 2 args[:offset] = 0
  36. 2 args[:limit] = 0
  37. 4 returning(:count, args) { search args }
  38. end
  39. # for override
  40. 1 def item_type
  41. nil
  42. end
  43. 1 def each_item_name_with_options _content=nil
  44. options = {}
  45. item = fetch_query.statement[:view]
  46. options[:view] = item if item
  47. item_names.each do |name|
  48. yield name, options
  49. end
  50. end
  51. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  52. 1 def search_with_params
  53. 29 search_with_rescue search_params
  54. end
  55. 1 def count_with_params
  56. 6 search_with_rescue search_params.merge(return: :count)
  57. end
  58. 1 def search_with_rescue query_args
  59. 35 card.cached_search query_args
  60. rescue Error::BadQuery => e
  61. Rails.logger.info "BadQuery: #{query_args}"
  62. e
  63. end
  64. 1 def implicit_item_view
  65. 37 view = voo_items_view || item_view_from_query || default_item_view
  66. 37 Card::View.normalize view
  67. end
  68. # override if query can specify item view
  69. 1 def item_view_from_query
  70. nil
  71. end
  72. 1 def with_results
  73. 11 return render_no_search_results if search_with_params.empty?
  74. 4 yield
  75. end
  76. end
  77. end;end;end;end;
  78. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/05_search.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/05_search/views.rb

60.47% lines covered

86 relevant lines. 52 lines covered and 34 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Search;
  3. # Set: Abstract (Search, Views)
  4. #
  5. 1 module Views;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/05_search/views.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 view :search_count, cache: :never do
  10. search_with_params.to_s
  11. end
  12. 1 view :search_error, cache: :never do
  13. sr_class = search_with_params.class.to_s
  14. %(#{sr_class} :: #{search_with_params.message} :: #{card.content})
  15. end
  16. 1 view :card_list, cache: :never do
  17. if search_with_params.empty?
  18. "no results"
  19. else
  20. search_with_params.map do |item_card|
  21. nest_item item_card
  22. end.join "\n"
  23. end
  24. end
  25. end
  26. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  27. 1 AUTOCOMPLETE_LIMIT = 8 # number of name suggestions for autocomplete text fields
  28. 1 def item_cards
  29. search_with_params
  30. end
  31. # NOCACHE because paging_urls is uncacheable hash and thus not safe to merge
  32. 1 view :molecule, cache: :never do
  33. molecule.merge render_paging_urls
  34. end
  35. # TODO: design better autocomplete API
  36. 1 view :name_complete, cache: :never do
  37. complete_search limit: AUTOCOMPLETE_LIMIT
  38. end
  39. 1 view :name_match, cache: :never do
  40. complete_or_match_search limit: AUTOCOMPLETE_LIMIT
  41. end
  42. 1 def complete_or_match_search limit: AUTOCOMPLETE_LIMIT, start_only: false
  43. 3 starts_with = complete_search limit: limit
  44. 3 return starts_with if start_only
  45. remaining_slots = limit - starts_with.size
  46. return starts_with if remaining_slots.zero?
  47. starts_with + match_search(not_names: starts_with, limit: remaining_slots)
  48. end
  49. 1 def complete_search limit: AUTOCOMPLETE_LIMIT
  50. 3 card.search name_cql(limit).merge(complete_cql)
  51. end
  52. 1 def match_search limit: AUTOCOMPLETE_LIMIT, not_names: []
  53. card.search name_cql(limit).merge(match_cql(not_names))
  54. end
  55. 1 def name_cql limit
  56. 3 { limit: limit, sort: "name", return: "name" }
  57. end
  58. 1 def complete_cql
  59. 3 { complete: term_param }
  60. end
  61. 1 def match_cql not_names
  62. cql = { name_match: term_param }
  63. cql[:name] = ["not in"] + not_names if not_names.any?
  64. cql
  65. end
  66. 1 def term_param
  67. params[:term]
  68. end
  69. end
  70. 2 module DataFormat; module_parent.send :register_set_format, Card::Format::DataFormat, self; extend Card::Set::AbstractFormat
  71. 1 view :card_list do
  72. search_with_params.map do |item_card|
  73. nest_item item_card
  74. end
  75. end
  76. end
  77. 2 module CsvFormat; module_parent.send :register_set_format, Card::Format::CsvFormat, self; extend Card::Set::AbstractFormat
  78. 1 view :core, :core, mod: All::AllCsv::CsvFormat
  79. 1 view :card_list do
  80. items = super()
  81. if depth.zero?
  82. title_row + items
  83. else
  84. items
  85. end
  86. end
  87. end
  88. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  89. 1 view :card_list, cache: :never do
  90. 11 with_results do
  91. 4 search_result_list "search-result-list" do |item_card|
  92. 37 card_list_item item_card
  93. end
  94. end
  95. end
  96. 1 view :select_item, cache: :never do
  97. wrap do
  98. haml :select_item
  99. end
  100. end
  101. 1 before :select_item do
  102. class_up "card-slot", "_filter-result-slot"
  103. end
  104. 1 view :checkbox_list, cache: :never do
  105. with_results do
  106. search_result_list "_search-checkbox-list pr-2" do |item_card|
  107. checkbox_item item_card
  108. end
  109. end
  110. end
  111. 1 view :no_search_results do
  112. 7 wrap_with :div, "", class: "search-no-results"
  113. end
  114. 1 private
  115. 1 def card_list_item item_card
  116. 37 nest_item item_card, size: voo.size do |rendered, item_view|
  117. 37 %(<div class="search-result-item item-#{item_view}">#{rendered}</div>)
  118. end
  119. end
  120. 1 def search_result_list klass
  121. 4 with_paging do
  122. 4 wrap_with :div, class: klass do
  123. 4 search_with_params.map do |item_card|
  124. 37 yield item_card
  125. end
  126. end
  127. end
  128. end
  129. 1 def checkbox_item item_card
  130. subformat(item_card).wrap do
  131. haml :checkbox_item, unique_id: unique_id, item_card: item_card
  132. end
  133. end
  134. 1 def closed_limit
  135. [search_params[:limit].to_i, Card.config.closed_search_limit].min
  136. end
  137. end
  138. end;end;end;end;end;
  139. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/05_search/views.rb ~~

card/tmpsets/set/mod009-card-mod-search/abstract/06_cql_search.rb

93.65% lines covered

63 relevant lines. 59 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (CqlSearch)
  4. #
  5. 1 module CqlSearch;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/abstract/06_cql_search.rb"; end
  8. 1 include_set Abstract::Search
  9. 1 def search args={}
  10. 23 with_skipping args do
  11. 23 query = fetch_query(args)
  12. # forces explicit limiting
  13. # can be 0 or less to force no limit
  14. 23 raise "OH NO.. no limit" unless query.mods[:limit]
  15. 23 query.run
  16. end
  17. end
  18. # for override, eg when required subqueries are known to be missing
  19. 1 def skip_search?
  20. 23 false
  21. end
  22. 1 def with_skipping args
  23. 23 skip_search? ? skipped_search_result(args) : yield
  24. end
  25. 1 def skipped_search_result args={}
  26. args[:return] == :count ? 0 : []
  27. end
  28. 1 def cache_query?
  29. 715 true
  30. end
  31. 1 def fetch_query args={}
  32. 47 @query = nil unless cache_query?
  33. 47 @query ||= {}
  34. 47 @query[args.to_s] ||= query(args.clone) # cache query
  35. end
  36. 1 def query args={}
  37. 43 Query.new standardized_query_args(args), name
  38. end
  39. 1 def standardized_query_args args
  40. 43 args = query_args(args).symbolize_keys
  41. 43 args[:context] ||= name
  42. 43 args
  43. end
  44. 1 def cql_hash
  45. 423 @cql_hash = nil unless cache_query?
  46. 423 @cql_hash ||= cql_content.merge filter_and_sort_cql
  47. end
  48. # override this to define search
  49. 1 def cql_content
  50. 245 @cql_content = nil unless cache_query?
  51. 245 @cql_content ||= begin
  52. 245 query = content
  53. 245 query = query.is_a?(Hash) ? query : parse_json_query(query)
  54. 245 query.symbolize_keys
  55. end
  56. end
  57. 1 def query_args args={}
  58. 41 cql_hash.merge args
  59. end
  60. 1 def parse_json_query query
  61. 247 empty_query_error! if query.empty?
  62. 247 JSON.parse query
  63. rescue JSON::ParserError
  64. raise Error::BadQuery, "Invalid JSON search query: #{query}"
  65. end
  66. 1 def empty_query_error!
  67. raise Error::BadQuery, "can't run search with empty content"
  68. end
  69. 1 def item_type
  70. 365 type = cql_hash[:type]
  71. 365 return if type.is_a?(Array) || type.is_a?(Hash)
  72. 365 type
  73. end
  74. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  75. 1 def default_limit
  76. card_content_limit || super
  77. end
  78. 1 def card_content_limit
  79. 10 card.cql_hash&.dig :limit
  80. end
  81. 1 def item_view_from_query
  82. 37 query_with_params.statement[:item]
  83. end
  84. 1 def query_with_params
  85. 90 @query_with_params ||= card.fetch_query search_params
  86. end
  87. 1 def limit
  88. 53 query_with_params.limit
  89. end
  90. end
  91. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  92. 1 def default_limit
  93. 10 card_content_limit || super
  94. end
  95. end
  96. end;end;end;end;
  97. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/abstract/06_cql_search.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/children.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Children" cards
  4. #
  5. 1 module Children;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/children.rb"; end
  8. 1 def raw_help_text
  9. "Cards formed by \"mating\" {{_left|name}} with another card. "\
  10. "eg: \"{{_left|name}}+mate\"."
  11. end
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/children.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/created.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Created" cards
  4. #
  5. 1 module Created;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/created.rb"; end
  8. 1 def raw_help_text
  9. "Cards created by {{_left|name}}."
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/created.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/edited.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Edited" cards
  4. #
  5. 1 module Edited;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/edited.rb"; end
  8. 1 def raw_help_text
  9. "Cards edited by {{_left|name}}."
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/edited.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/editors.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Editors" cards
  4. #
  5. 1 module Editors;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/editors.rb"; end
  8. 1 def raw_help_text
  9. "Users who have edited {{_left|name}}."
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/editors.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/follow.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Follow" cards
  4. #
  5. 1 module Follow;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/follow.rb"; end
  8. 1 def raw_help_text
  9. "Get notified about changes"
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/follow.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/linked_to_by.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+LinkedToBy" cards
  4. #
  5. 1 module LinkedToBy;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/linked_to_by.rb"; end
  8. 1 def raw_help_text
  9. "Cards that link to {{_left|name}}."
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/linked_to_by.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/links_to.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+LinksTo" cards
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module LinksTo;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/links_to.rb"; end
  9. 1 def raw_help_text
  10. "Cards that <em>{{_left|name}}</em> links to."
  11. end
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/links_to.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/mates.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Mates" cards
  4. #
  5. 1 module Mates;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/mates.rb"; end
  8. 1 def raw_helo_text
  9. "If there is a card named \"X+{{_left|name}}\", then X is a mate of {{_left|name}}."
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/mates.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/nested_by.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+NestedBy" cards
  4. #
  5. 1 module NestedBy;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/nested_by.rb"; end
  8. 1 def raw_help_text
  9. "Cards that include {{_left|name}}."
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/nested_by.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/nests.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Nests" cards
  4. #
  5. 1 module Nests;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/nests.rb"; end
  8. 1 def raw_help_text
  9. "Cards that {{_left|name}} includes."
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/nests.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/referred_to_by.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+ReferredToBy" cards
  4. #
  5. 1 module ReferredToBy;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/referred_to_by.rb"; end
  8. 1 def raw_help_text
  9. "Cards that refer to {{_left|name}}."
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/referred_to_by.rb ~~

card/tmpsets/set/mod009-card-mod-search/right/refers_to.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+RefersTo" cards
  4. #
  5. 1 module RefersTo;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/right/refers_to.rb"; end
  8. 1 def raw_help_text
  9. "Cards that {{_left|name}} refers to."
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/right/refers_to.rb ~~

card/tmpsets/set/mod009-card-mod-search/self/recent.rb

66.67% lines covered

24 relevant lines. 16 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Recent"
  4. #
  5. 1 module Recent;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/self/recent.rb"; end
  8. 1 ACTS_PER_PAGE = 25
  9. 1 view :title do
  10. 224 voo.title ||= "Recent Changes"
  11. 224 super()
  12. end
  13. 1 def recent_acts
  14. action_relation = qualifying_actions.where "card_acts.id = card_act_id"
  15. Act.where("EXISTS (#{action_relation.to_sql})").order id: :desc
  16. end
  17. 1 def qualifying_actions
  18. Action.all_viewable.where "draft is not true"
  19. end
  20. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  21. 1 view :core do
  22. voo.hide :history_legend unless voo.main
  23. @acts_per_page = ACTS_PER_PAGE
  24. acts_layout card.recent_acts, :absolute
  25. end
  26. end
  27. 2 module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
  28. 1 view :feed_item_description do
  29. render_blank
  30. end
  31. end
  32. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  33. 1 def items_for_export
  34. card.item_cards limit: 20
  35. end
  36. end
  37. end;end;end;end;
  38. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/self/recent.rb ~~

card/tmpsets/set/mod009-card-mod-search/self/search.rb

86.54% lines covered

52 relevant lines. 45 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Search"
  4. #
  5. 1 module Search;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/self/search.rb"; end
  8. 1 def query_args args={}
  9. 10 return super unless keyword_contains_cql? args
  10. 2 args.merge parse_keyword_cql(args)
  11. end
  12. 1 def parse_keyword_cql args
  13. 2 parse_json_query(args[:vars][:keyword])
  14. end
  15. 1 def keyword_contains_cql? hash
  16. 10 hash[:vars] && (keyword = hash[:vars][:keyword]) && keyword =~ /^\{.+\}$/
  17. end
  18. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  19. 1 view :search_error, cache: :never do
  20. sr_class = search_with_params.class.to_s
  21. # don't show card content; not very helpful in this case
  22. %(#{sr_class} :: #{search_with_params.message})
  23. end
  24. end
  25. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  26. 1 view :title, cache: :never do
  27. 4 return super() unless (keyword = search_keyword) &&
  28. 4 (title = keyword_search_title(keyword))
  29. 4 voo.title = title
  30. end
  31. 1 def keyword_search_title keyword
  32. 4 %(Search results for: <span class="search-keyword">#{h keyword}</span>)
  33. end
  34. 1 def search_keyword
  35. 4 (vars = search_vars) && vars[:keyword]
  36. rescue Card::Error::PermissionDenied
  37. nil
  38. end
  39. 1 def search_vars
  40. 4 root.respond_to?(:search_params) ? root.search_params[:vars] : search_params[:vars]
  41. end
  42. 1 def cql_search?
  43. card.keyword_contains_cql? vars: search_vars
  44. end
  45. end
  46. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  47. 1 view :complete, cache: :never do
  48. 3 term = term_param
  49. 3 exact = Card.fetch term, new: {}
  50. {
  51. 3 search: true,
  52. term: term,
  53. add: add_item(exact),
  54. new: new_item_of_type(exact),
  55. goto: goto_items(term, exact)
  56. }
  57. end
  58. 1 def add_item exact
  59. 3 return unless exact.new_card? &&
  60. exact.name.valid? &&
  61. !exact.virtual? &&
  62. exact.ok?(:create)
  63. [h(exact.name), exact.name.url_key]
  64. end
  65. 1 def new_item_of_type exact
  66. 3 return unless (exact.type_id == CardtypeID) &&
  67. Card.new(type_id: exact.id).ok?(:create)
  68. [exact.name, "new/#{exact.name.url_key}"]
  69. end
  70. 1 def goto_items term, exact
  71. 3 goto_names = complete_or_match_search start_only: Card.config.navbox_match_start_only
  72. 3 goto_names.unshift exact.name if add_exact_to_goto_names? exact, goto_names
  73. 3 goto_names.map do |name|
  74. 6 [name, name.to_name.url_key, h(highlight(name, term, sanitize: false))]
  75. end
  76. end
  77. 1 def add_exact_to_goto_names? exact, goto_names
  78. 4 exact.known? && !goto_names.find { |n| n.to_name.key == exact.key }
  79. end
  80. 1 def term_param
  81. 6 term = query_params[:keyword]
  82. 6 if (term =~ /^\+/) && (main = params["main"])
  83. term = main + term
  84. end
  85. 6 term
  86. end
  87. end
  88. end;end;end;end;
  89. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/self/search.rb ~~

card/tmpsets/set/mod009-card-mod-search/type/search_type.rb

54.35% lines covered

46 relevant lines. 25 lines covered and 21 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "SearchType" cards
  4. #
  5. 1 module SearchType;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-search/set/type/search_type.rb"; end
  8. 1 include_set Type::Json
  9. 1 include_set Abstract::CqlSearch
  10. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  11. 1 view :core, cache: :never do
  12. _render search_result_view
  13. end
  14. 1 def chunk_list
  15. 64 :query
  16. end
  17. 1 def search_result_view
  18. 11 case search_with_params
  19. when Exception then :search_error
  20. when Integer then :search_count
  21. when nest_mode == :template then :raw
  22. 11 else :card_list
  23. end
  24. end
  25. end
  26. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  27. 1 def items_for_export
  28. return [] if card.content.empty? || unexportable_tag?(card.name.tag_name.key)
  29. card.item_cards
  30. end
  31. # avoid running the search from +:content_options (huge results)
  32. # and +:structure (errors)
  33. # TODO: make this configurable in set mods
  34. 1 def unexportable_tag? tag_key
  35. %i[content_options structure].map { |code| code.cardname.key }.include? tag_key
  36. end
  37. end
  38. 2 module RssFormat; module_parent.send :register_set_format, Card::Format::RssFormat, self; extend Card::Set::AbstractFormat
  39. 1 view :feed_body do
  40. case raw_feed_items
  41. when Exception then @xml.item(render!(:search_error))
  42. when Integer then @xml.item(render!(:search_count))
  43. else super()
  44. end
  45. end
  46. 1 def raw_feed_items
  47. @raw_feed_items ||= search_with_params
  48. end
  49. end
  50. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  51. 1 view :core do
  52. 11 _render search_result_view
  53. end
  54. 1 view :bar do
  55. voo.hide :one_line_content
  56. super()
  57. end
  58. 1 view :one_line_content, cache: :never do
  59. if depth > max_depth
  60. "..."
  61. else
  62. search_params[:limit] = closed_limit
  63. _render_core hide: "paging", items: { view: :link }
  64. # TODO: if item is queryified to be "name", then that should work.
  65. # otherwise use link
  66. end
  67. end
  68. 1 def rss_link_tag
  69. path_opts = { format: :rss }
  70. Array(search_params[:vars]).compact.each { |k, v| opts["_#{k}"] = v }
  71. tag "link", rel: "alternate",
  72. type: "application/rss+xml",
  73. title: "RSS",
  74. href: path(path_opts)
  75. end
  76. end
  77. end;end;end;end;
  78. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-search/set/type/search_type.rb ~~

card/tmpsets/set/mod010-standard/all/comment.rb

44.44% lines covered

45 relevant lines. 20 lines covered and 25 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Comment)
  4. #
  5. 1 module Comment;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/comment.rb"; end
  8. 1 def commenting?
  9. 297 comment && action != :delete
  10. end
  11. 1 event :add_comment, :prepare_to_store, on: :save, when: :comment do
  12. Env.session[:comment_author] = comment_author if Env.session
  13. return unless comment.present?
  14. self.content =
  15. [content, format.comment_with_signature].compact.join "\n<hr\>\n"
  16. self.comment = nil
  17. end
  18. 1 attr_writer :comment_author
  19. 1 def comment_author
  20. @comment_author ||=
  21. Env.session[:comment_author] || Env.params[:comment_author] || "Anonymous"
  22. end
  23. 1 def clean_comment
  24. comment.split(/\n/).map do |line|
  25. "<p>#{line.strip.empty? ? '&nbsp;' : line}</p>"
  26. end * "\n"
  27. end
  28. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  29. 1 def comment_with_signature
  30. card.clean_comment + "\n" + comment_signature
  31. end
  32. 1 def comment_signature
  33. wrap_with :div, class: "w-comment-author" do
  34. "#{comment_author}.....#{Time.zone.now}"
  35. end
  36. end
  37. 1 def comment_author
  38. if Auth.signed_in?
  39. "[[#{Auth.current.name}]]"
  40. else
  41. "#{card.comment_author} (Not signed in)"
  42. end
  43. end
  44. 1 view :comment_box, denial: :blank, unknown: true, perms: :update do
  45. wrap_with :div, class: "comment-box nodblclick" do
  46. action = card.new_card? ? :create : :update
  47. card_form action do
  48. [hidden_comment_fields, comment_box, comment_buttons]
  49. end
  50. end
  51. end
  52. 1 def hidden_comment_fields
  53. return unless card.new_card?
  54. hidden_field_tag "card[name]", card.name
  55. # FIXME: wish we had more generalized solution for names.
  56. # without this, nonexistent cards will often take left's linkname.
  57. # (needs test)
  58. end
  59. 1 def comment_box
  60. text_area :comment, rows: 3
  61. end
  62. 1 def comment_buttons
  63. wrap_with :div, class: "comment-buttons" do
  64. [comment_author_label, comment_submit_button]
  65. end
  66. end
  67. 1 def comment_author_label
  68. return if Auth.signed_in?
  69. %(<label>My Name is:</label> #{text_field :comment_author})
  70. end
  71. 1 def comment_submit_button
  72. submit_button text: "Comment", type: :submit, disable_with: "Commenting"
  73. end
  74. end
  75. end;end;end;end;
  76. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/comment.rb ~~

card/tmpsets/set/mod010-standard/all/error.rb

54.55% lines covered

44 relevant lines. 24 lines covered and 20 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Error)
  4. #
  5. 1 module Error;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/error.rb"; end
  8. 1 def copy_errors card
  9. card.errors.each do |att, msg|
  10. errors.add att, msg
  11. end
  12. end
  13. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  14. 1 view :compact_missing, perms: :none, compact: true do
  15. ""
  16. end
  17. 1 view :unknown, perms: :none, cache: :never do
  18. ""
  19. end
  20. 1 view :server_error, perms: :none do
  21. tr(:server_error)
  22. end
  23. 1 view :denial, perms: :none do
  24. focal? ? tr(:denial) : ""
  25. end
  26. 1 view :not_found, perms: :none do
  27. error_name = card.name.present? ? safe_name : tr(:not_found_no_name)
  28. tr(:not_found_named, cardname: error_name)
  29. end
  30. 1 view :bad_address, perms: :none do
  31. root.error_status = 404
  32. tr(:bad_address)
  33. end
  34. 1 view :errors do
  35. ["Problem:", "", standard_errors].flatten.join "\n"
  36. end
  37. 1 def standard_errors
  38. card.errors.map do |attrib, msg|
  39. attrib == :abort ? msg : standard_error_message(attrib, msg)
  40. end
  41. end
  42. # for override
  43. 1 def standard_error_message attribute, message
  44. "#{attribute.to_s.upcase}: #{message}"
  45. end
  46. 1 def unsupported_view_error_message view
  47. tr(:unsupported_view, view: view, cardname: card.name)
  48. end
  49. end
  50. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  51. 1 view :errors do
  52. format_error error_list
  53. end
  54. 1 view :server_error, :errors
  55. 1 view :denial, :errors
  56. 1 view :not_found, :errors
  57. 1 view :bad_address do
  58. format_error super()
  59. end
  60. 1 def format_error error
  61. { error_status: error_status, errors: error }
  62. end
  63. 1 def error_list
  64. card.errors.each_with_object([]) do |(field, message), list|
  65. list << { field: field, message: message }
  66. end
  67. end
  68. end
  69. end;end;end;end;
  70. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/error.rb ~~

card/tmpsets/set/mod010-standard/all/links.rb

88.73% lines covered

71 relevant lines. 63 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Links)
  4. #
  5. 1 module Links;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/links.rb"; end
  8. 1 RESOURCE_TYPE_REGEXP = /^([a-zA-Z][\-+\.a-zA-Z\d]*):/
  9. # The #link_to methods support smart formatting of links in multiple formats.
  10. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  11. # Creates a "link", the meaning of which depends upon the format. In this base
  12. # format, the link looks like [text][absolute path]
  13. #
  14. # @param text [String] optional string associated with link
  15. # @param opts [Hash] optional Hash. In simple formats, :path is usually the only key
  16. 1 def link_to text=nil, opts={}
  17. path = path((opts.delete(:path) || {}))
  18. if text && path != text
  19. "#{text}[#{path}]"
  20. else
  21. path
  22. end
  23. end
  24. # link to a different view of the current card
  25. # @param view [Symbol,String]
  26. # @param text [String]
  27. # @param opts [Hash]
  28. 1 def link_to_view view, text=nil, opts={}
  29. 791 add_to_path opts, view: view unless view == :home
  30. 791 link_to text, opts
  31. end
  32. # link to a card other than the current card.
  33. # @param cardish [Integer, Symbol, String, Card] a card identifier
  34. # @param text [String]
  35. # @param opts [Hash]
  36. 1 def link_to_card cardish, text=nil, opts={}
  37. 2923 add_to_path opts, mark: Card::Name[cardish]
  38. 2923 link_to text, opts
  39. end
  40. # a "resource" is essentially a reference to something that
  41. # decko doesn't recognize to be a card. Can be a remote url,
  42. # a local url (that decko hasn't parsed) or a local path.
  43. # @param resource [String]
  44. # @param text [String]
  45. # @param opts [Hash]
  46. 1 def link_to_resource resource, text=nil, opts={}
  47. 715 resource = clean_resource resource, resource_type(resource)
  48. 715 link_to text, opts.merge(path: resource)
  49. end
  50. # smart_link_to is wrapper method for #link_to, #link_to_card, #link_to_view, and
  51. # #link_to_resource. If the opts argument contains :view, :related, :card, or
  52. # :resource, it will use the respective method to render a link.
  53. #
  54. # This is usually most useful when writing views that generate many different
  55. # kinds of links.
  56. 1 def smart_link_to text, opts={}
  57. if (linktype = %i[view card resource].find { |key| opts[key] })
  58. send "link_to_#{linktype}", opts.delete(linktype), text, opts
  59. else
  60. send :link_to, text, opts
  61. end
  62. end
  63. 1 private
  64. 1 def resource_type resource
  65. 1430 case resource
  66. 508 when /^https?\:/ then "external-link"
  67. 910 when %r{^/} then "internal-link"
  68. 12 when /^mailto\:/ then "email-link"
  69. when RESOURCE_TYPE_REGEXP then Regexp.last_match(1) + "-link"
  70. end
  71. end
  72. 1 def clean_resource resource, resource_type
  73. 715 if resource_type == "internal-link"
  74. 455 contextualize_path resource[1..-1]
  75. else
  76. 260 resource
  77. end
  78. end
  79. 1 def add_to_path opts, new_hash
  80. 3714 opts[:path] = (opts[:path] || {}).merge new_hash
  81. end
  82. end
  83. 1 public
  84. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  85. # in HTML, #link_to renders an anchor tag <a>
  86. # it treats opts other than "path" as html opts for that tag,
  87. # and it adds special handling of "remote" and "method" opts
  88. # (changes them into data attributes)
  89. 1 def link_to text=nil, opts={}
  90. 5028 opts[:href] ||= path opts.delete(:path)
  91. 5028 text = raw(text || opts[:href])
  92. 5028 interpret_data_opts_to_link_to opts
  93. 5028 wrap_with :a, text, opts
  94. end
  95. # in HTML, #link_to_card adds special css classes indicated whether a
  96. # card is "known" (real or virtual) or "wanted" (unknown)
  97. # TODO: upgrade from (known/wanted)-card to (real/virtual/unknown)-card
  98. 1 def link_to_card cardish, text=nil, opts={}
  99. 2923 name = Card::Name[cardish]
  100. 2923 slotterify opts if opts[:slotter]
  101. 2923 add_known_or_wanted_class opts, name
  102. 2923 super name, (text || name), opts
  103. end
  104. # in HTML, #link_to_view defaults to a remote link with rel="nofollow".
  105. 1 def link_to_view view, text=nil, opts={}
  106. 791 slotterify opts
  107. 791 super view, (text || view), opts
  108. end
  109. # in HTML, #link_to_resource automatically adds a target to external resources
  110. # so they will open in another tab. It also adds css classes indicating whether
  111. # the resource is internal or external
  112. 1 def link_to_resource resource, text=nil, opts={}
  113. 715 add_resource_opts opts, resource_type(resource)
  114. 715 super
  115. end
  116. 1 private
  117. 1 def slotterify opts
  118. 791 opts.delete(:slotter)
  119. 791 opts.reverse_merge! remote: true, rel: "nofollow"
  120. 791 add_class opts, "slotter"
  121. end
  122. 1 def add_known_or_wanted_class opts, name
  123. 2923 known = opts.delete :known
  124. 2923 known = Card.known?(name) if known.nil?
  125. 2923 add_class opts, (known ? "known-card" : "wanted-card")
  126. end
  127. 1 def interpret_data_opts_to_link_to opts
  128. 5028 %i[remote method].each do |key|
  129. 10056 next unless (val = opts.delete key)
  130. 838 opts["data-#{key}"] = val
  131. end
  132. end
  133. 1 def add_resource_opts opts, resource_type
  134. 715 opts[:target] ||= "_blank" if resource_type == "external-link"
  135. 715 add_class opts, resource_type
  136. end
  137. end
  138. end;end;end;end;
  139. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/links.rb ~~

card/tmpsets/set/mod010-standard/all/list_changes.rb

73.08% lines covered

26 relevant lines. 19 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (ListChanges)
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module ListChanges;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/list_changes.rb"; end
  9. 1 def list_fields
  10. 1 Card.search({ left: name, type_id: Card::MirroredListID }, "list fields")
  11. end
  12. 1 def listed_by_fields
  13. 1 Card.search({ left: name, type_id: Card::MirrorListID }, "listed by fields")
  14. end
  15. 1 def linker_lists
  16. 136 Card.search({ type_id: Card::MirroredListID, link_to: name },
  17. "lists that link to #{name}")
  18. end
  19. 1 def codename_list_exist?
  20. 137 Card::Codename.exists?(:mirrored_list) && Card::Codename.exists?(:mirror_list)
  21. end
  22. 1 event :trunk_cardtype_of_a_list_relation_changed, :finalize,
  23. changed: :type, on: :update, when: :codename_list_exist? do
  24. 1 type_key_was = Card.quick_fetch(type_id_before_act)&.key
  25. 1 list_fields.each do |card|
  26. card.update_listed_by_cache_for card.item_keys, type_key: type_key_was
  27. card.update_listed_by_cache_for card.item_keys
  28. end
  29. 1 listed_by_fields.each &:update_cached_list
  30. end
  31. 1 event :trunk_name_of_a_list_relation_changed, :finalize,
  32. changed: :name, on: :update,
  33. when: :codename_list_exist? do
  34. list_fields.each do |card|
  35. card.update_listed_by_cache_for card.item_keys
  36. end
  37. listed_by_fields.each &:update_cached_list
  38. end
  39. 1 event :cardtype_of_list_item_changed, :validate,
  40. changed: :type, on: :save,
  41. when: :codename_list_exist? do
  42. 136 linker_lists.each do |card|
  43. next unless card.item_type_id != type_id
  44. errors.add(:type,
  45. "can't be changed because #{name} " \
  46. "is referenced by list card #{card.name}")
  47. end
  48. end
  49. end;end;end;end;
  50. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/list_changes.rb ~~

card/tmpsets/set/mod010-standard/all/path.rb

96.1% lines covered

77 relevant lines. 74 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Path)
  4. #
  5. 1 module Path;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/path.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. # Decko uses "path" a bit unusually. In most formats, it returns a full url. In HTML,
  10. # it provides everything after the domain/port.
  11. #
  12. # If you're feeling your saucy oats, you might point out that typically "paths" don't
  13. # include queries and fragment identifiers, much less protocols, domains, and ports.
  14. # 10 pedantry points to you! But "path" has just four letters and is smart about
  15. # format needs, so using it will lead you down the right ... something or other.
  16. # Format#path is for generating standard card routes, eg, assuming the card
  17. # associated with the current format is named "current", it will generate paths like
  18. # these:
  19. # path view: :bar -> "current?view=bar"
  20. # path mark: [mycardid] -> "mycardname"
  21. # path format: :csv) -> "current.csv"
  22. # path action: :update -> "update/current"
  23. # #path produces paths that follow one of three main patterns:
  24. # 1. mark[.format][?query] # standard GET request
  25. # 2. action/mark[?query] # GET variant of standard actions
  26. # 3. new/mark # shortcut for "new" view of cardtype
  27. # @param opts [Hash, String] a String is treated as a complete path and
  28. # bypasses all processing
  29. # @option opts [String, Card::Name, Integer, Symbol, Card] :mark
  30. # @option opts [Symbol] :action card action (:create, :update, :delete)
  31. # @option opts [Symbol] :format
  32. # @option opts [Hash] :card
  33. # @option opts [TrueClass] :no_mark
  34. 1 CAST_PARAMS = { slot: { hide: :array, show: :array, wrap: :array } }.freeze
  35. # TODO: monkey API for this
  36. 1 def path opts={}
  37. 6055 return opts unless opts.is_a? Hash
  38. 5052 path = new_cardtype_path(opts) || standard_path(opts)
  39. 5052 contextualize_path path
  40. end
  41. # in base format (and therefore most other formats), even internal paths
  42. # are rendered as absolute urls.
  43. 1 def contextualize_path relative_path
  44. 19 card_url relative_path
  45. end
  46. 1 private
  47. 1 def new_cardtype_path opts
  48. 5052 return unless valid_opts_for_new_cardtype_path? opts
  49. 39 "#{opts.delete :action}/#{path_mark opts}#{path_query opts}"
  50. end
  51. 1 def valid_opts_for_new_cardtype_path? opts
  52. 5052 return unless opts[:action].in? %i[new type]
  53. # "new" and "type" are not really an action and are only
  54. # a valid value here for this path
  55. 39 opts[:mark].present?
  56. end
  57. 1 def standard_path opts
  58. 5013 path_base(opts) + path_extension(opts) + path_query(opts)
  59. end
  60. 1 def path_base opts
  61. 5013 mark = path_mark opts
  62. 5013 if (action = path_action opts)
  63. 611 action_base action, mark
  64. else
  65. 4402 mark
  66. end
  67. end
  68. 1 def action_base action, mark
  69. 611 mark.present? ? "#{action}/#{mark}" : "card/#{action}"
  70. # the card/ prefix prevents interpreting action as cardname
  71. end
  72. 1 def path_action opts
  73. 5013 return unless (action = opts.delete(:action)&.to_sym)
  74. 2005 %i[create update delete].find { |a| a == action }
  75. end
  76. 1 def path_mark opts
  77. 5052 return "" if markless_path? opts
  78. 5001 name = opts[:mark] ? Card::Name[opts.delete(:mark)] : card.name
  79. 5001 add_unknown_name_to_opts name.to_name, opts
  80. 5001 name.to_name.url_key
  81. end
  82. 1 def markless_path? opts
  83. 5052 opts[:action] == :create || opts.delete(:no_mark)
  84. end
  85. 1 def path_extension opts
  86. 5013 extension = opts.delete :format
  87. 5013 extension ? ".#{extension}" : ""
  88. end
  89. 1 def path_query opts
  90. 5052 opts = cast_path_opts opts
  91. 5052 opts.empty? ? "" : "?#{opts.to_param}"
  92. end
  93. # normalizes certain path opts to specified data types
  94. 1 def cast_path_opts opts, cast_hash=nil
  95. 5130 cast_hash ||= CAST_PARAMS
  96. 5130 return opts unless opts.is_a?(::Hash)
  97. 5130 opts.each do |key, value|
  98. 1615 next unless (cast_to = cast_hash[key])
  99. 156 opts[key] = cast_path_value value, cast_to
  100. end
  101. end
  102. 1 def cast_path_value value, cast_to
  103. 156 if cast_to.is_a? Hash
  104. 78 cast_path_opts value, cast_to
  105. else
  106. 78 send "cast_path_value_as_#{cast_to}", value
  107. end
  108. end
  109. 1 def cast_path_value_as_array value
  110. 78 Array.wrap value
  111. end
  112. 1 def add_unknown_name_to_opts name, opts
  113. 5001 return if name_specified?(opts) || name_standardish?(name) || Card.known?(name)
  114. 13 opts[:card] ||= {}
  115. 13 opts[:card][:name] = name
  116. end
  117. 1 def name_specified? opts
  118. 5001 opts[:card] && opts[:card][:name]
  119. end
  120. # no name info will be lost by using url_key
  121. 1 def name_standardish? name
  122. 5001 name.s == Card::Name.url_key_to_standard(name.url_key)
  123. end
  124. end
  125. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  126. 1 def add_unknown_name_to_opts name, opts
  127. # noop
  128. end
  129. end
  130. 2 module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
  131. 1 def contextualize_path relative_path
  132. if Card.config.file_storage == :local
  133. # absolute paths lead to invalid assets path in css for cukes
  134. card_path relative_path
  135. else
  136. # ...but relative paths are problematic when machine output and
  137. # hard-coded assets (like fonts) are on different servers
  138. card_url relative_path
  139. end
  140. end
  141. end
  142. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  143. # in HTML, decko paths rendered as relative to the site's root.
  144. 1 def contextualize_path relative_path
  145. 5697 card_path relative_path
  146. end
  147. end
  148. 2 module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
  149. 1 def contextualize_path relative_path
  150. 53 card_url relative_path
  151. end
  152. end
  153. end;end;end;end;
  154. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/path.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (RichHtml)
  4. #
  5. 1 module RichHtml;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 delegate :class_up, :class_down, :with_class_up, :without_upped_class, :classy,
  10. to: :voo
  11. end
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/alert.rb

100.0% lines covered

17 relevant lines. 17 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Alert)
  4. #
  5. 1 module Alert;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/alert.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # alert_types: 'success', 'info', 'warning', 'danger'
  10. 1 def alert alert_type, dismissable=false, disappear=false, args={}
  11. 53 add_class args, alert_classes(alert_type, dismissable, disappear)
  12. 53 wrap_with :div, args.merge(role: "alert") do
  13. 53 [(alert_close_button if dismissable), output(yield)]
  14. end
  15. end
  16. 1 def alert_classes alert_type, dismissable, disappear
  17. 53 classes = ["alert", "alert-#{alert_type}"]
  18. 53 classes << "alert-dismissible " if dismissable
  19. 53 classes << "_disappear" if disappear
  20. 53 classy classes
  21. end
  22. 1 def alert_close_button
  23. 51 wrap_with :button, type: "button", "data-dismiss": "alert",
  24. class: "close", "aria-label": "Close" do
  25. 51 wrap_with :span, "&times;", "aria-hidden" => true
  26. end
  27. end
  28. end
  29. end;end;end;end;end;
  30. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/alert.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/content.rb

63.92% lines covered

97 relevant lines. 62 lines covered and 35 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Content)
  4. #
  5. 1 module Content;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/content.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def prepare_content_slot
  10. 253 class_up "card-slot", "d0-card-content"
  11. 253 voo.hide :menu
  12. end
  13. 254 before(:content) { prepare_content_slot }
  14. 1 view :content do
  15. 246 voo.hide :edit_button
  16. 246 wrap do
  17. 246 [_render_menu, _render_core, _render_edit_button(edit: :inline)]
  18. end
  19. end
  20. 1 before(:content_with_edit_button) do
  21. prepare_content_slot
  22. end
  23. 1 view :content_with_edit_button do
  24. wrap do
  25. [_render_menu, _render_core, _render_edit_button(edit: :inline)]
  26. end
  27. end
  28. 1 view :short_content, wrap: { div: { class: "text-muted" } } do
  29. 48 short_content
  30. end
  31. 1 view :raw_one_line_content, unknown: :mini_unknown,
  32. wrap: { div: { class: "text-muted" } } do
  33. raw_one_line_content
  34. end
  35. 1 view :one_line_content, unknown: :mini_unknown,
  36. wrap: { div: { class: "text-muted" } } do
  37. one_line_content
  38. end
  39. 1 before(:content_with_title) { prepare_content_slot }
  40. 1 view :content_with_title do
  41. wrap true, title: card.format(:text).render_core do
  42. [_render_menu, _render_core]
  43. end
  44. end
  45. 1 before :content_panel do
  46. prepare_content_slot
  47. class_up "card-slot", "card"
  48. end
  49. 1 view :content_panel do
  50. wrap do
  51. wrap_with :div, class: "card-body" do
  52. [_render_menu, _render_core]
  53. end
  54. end
  55. end
  56. 1 view :titled do
  57. 248 @content_body = true
  58. 248 wrap do
  59. [
  60. 496 naming { render_header },
  61. render_flash,
  62. 248 wrap_body { render_titled_content },
  63. render_comment_box(optional: :hide)
  64. ]
  65. end
  66. end
  67. 1 view :labeled, unknown: true do
  68. 6 @content_body = true
  69. 6 wrap(true, class: "row") do
  70. 12 labeled(render_title, wrap_body { "#{render_menu}#{render_labeled_content}" } )
  71. end
  72. end
  73. 1 def labeled label, content
  74. 6 haml :labeled, label: label, content: content
  75. end
  76. 1 def labeled_field field, item_view=:name, opts={}
  77. opts[:title] ||= Card.fetch_name field
  78. field_nest field, opts.merge(view: :labeled,
  79. items: (opts[:items] || {}).merge(view: item_view))
  80. end
  81. 1 view :open do
  82. toggle_logic
  83. @toggle_mode = :open
  84. @content_body = true
  85. frame do
  86. [_render_open_content, render_comment_box(optional: :hide)]
  87. end
  88. end
  89. 1 view :closed do
  90. with_nest_mode :compact do
  91. toggle_logic
  92. class_up "d0-card-body", "closed-content"
  93. @content_body = false
  94. @toggle_mode = :close
  95. frame
  96. end
  97. end
  98. 1 def toggle_logic
  99. show_view?(:title_link, :hide) ? voo.show(:icon_toggle) : voo.show(:title_toggle)
  100. end
  101. 1 def current_set_card
  102. set_name = params[:current_set]
  103. set_name ||= "#{card.name}+*type" if card.known? && card.type_id == Card::CardtypeID
  104. set_name ||= "#{card.name}+*self"
  105. Card.fetch(set_name)
  106. end
  107. 1 def raw_one_line_content
  108. cleaned = Card::Content.clean! render_raw, {}
  109. cut_with_ellipsis cleaned
  110. end
  111. 1 def one_line_content
  112. # TODO: use a version of Card::Content.smart_truncate
  113. # that counts characters instead of clean!
  114. cleaned = Card::Content.clean! render_core, {}
  115. cut_with_ellipsis cleaned
  116. end
  117. # LOCALIZE
  118. 1 def short_content
  119. 42 short_content_items || short_content_fields || short_content_from_core
  120. end
  121. 1 def short_content_items
  122. 42 return unless card.respond_to? :count
  123. 7 "#{count} #{'item'.pluralize count}"
  124. end
  125. 1 def short_content_fields
  126. 35 with_short_content_fields do |num_fields|
  127. 6 "#{num_fields} #{'field'.pluralize num_fields}" if num_fields.positive?
  128. end
  129. end
  130. 1 def with_short_content_fields
  131. 35 yield nested_field_names.size if voo.structure || card.structure
  132. end
  133. 1 def short_content_from_core
  134. 29 content = render_core
  135. 29 if content.blank?
  136. 12 "empty"
  137. 17 elsif content.size <= 5
  138. 2 content
  139. 15 elsif content.count("\n") < 2
  140. 14 "#{content.size} characters"
  141. else
  142. 1 "#{content.count("\n") + 1} lines"
  143. end
  144. end
  145. 1 def count
  146. 15 @count ||= card.count
  147. end
  148. end
  149. end;end;end;end;end;
  150. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/content.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/error.rb

59.18% lines covered

98 relevant lines. 58 lines covered and 40 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Error)
  4. #
  5. 1 module Error;
  6. 1 extend Card::Set
  7. 2 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/error.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :server_error, template: :haml
  10. 1 view :debug_server_error, wrap: { modal: { size: :full } } do
  11. error_page = BetterErrors::ErrorPage.new Card::Error.current,
  12. "PATH_INFO" => request.env["REQUEST_URI"]
  13. haml :debug_server_error, {}, error_page
  14. end
  15. 1 view :unknown do
  16. 75 createable { wrap { unknown_link "#{unknown_icon} #{render_title}" } }
  17. end
  18. # icon only, no wrap
  19. 1 view :mini_unknown, unknown: true, cache: :never do
  20. createable { unknown_link unknown_icon }
  21. end
  22. 1 def createable
  23. 25 card.ok?(:create) ? yield : ""
  24. end
  25. 1 def unknown_link text
  26. 25 path_opts = voo.type ? { card: { type: voo.type } } : {}
  27. 25 link_to_view :new_in_modal, text, path: path_opts, class: classy("unknown-link")
  28. end
  29. 1 def unknown_icon
  30. 25 fa_icon "plus-square"
  31. end
  32. 1 view :compact_missing, perms: :none do
  33. wrap_with :span, h(title_in_context), class: "text-muted"
  34. end
  35. 1 view :conflict, cache: :never do
  36. 2 actor_link = link_to_card card.last_action.act.actor.name
  37. 2 class_up "card-slot", "error-view"
  38. 2 wrap do # LOCALIZE
  39. 2 alert "warning" do
  40. 2 %(
  41. <strong>Conflict!</strong>
  42. <span class="new-current-revision-id">#{card.last_action_id}</span>
  43. <div>#{actor_link} has also been making changes.</div>
  44. <div>Please examine below, resolve above, and re-submit.</div>
  45. #{render_act}
  46. )
  47. end
  48. end
  49. end
  50. 1 view :errors, perms: :none do
  51. 1 return if card.errors.empty?
  52. 1 voo.title = card.name.blank? ? "Problems" : tr(:problems_name, cardname: card.name)
  53. 1 voo.hide! :menu
  54. 1 class_up "alert", "card-error-msg"
  55. 1 standard_errors voo.title
  56. end
  57. 1 view :not_found do
  58. voo.hide! :menu
  59. voo.title = "Not Found"
  60. frame do
  61. [not_found_errors, sign_in_or_up_links("to create it")]
  62. end
  63. end
  64. 1 view :denial do
  65. focal? ? loud_denial : quiet_denial
  66. end
  67. 1 def view_for_unknown view
  68. 29 main? && ok?(:create) ? :new : super
  69. end
  70. 1 def show_all_errors?
  71. # make configurable by env
  72. Auth.always_ok? || Rails.env.development?
  73. end
  74. 1 def error_cardname exception
  75. cardname = super
  76. show_all_errors? ? backtrace_link(cardname, exception) : cardname
  77. end
  78. 1 def rendering_error exception, view
  79. wrap_with(:span, class: "render-error alert alert-danger") { super }
  80. end
  81. 1 def error_modal_id
  82. @error_modal_id ||= unique_id
  83. end
  84. 1 def error_message exception
  85. %{
  86. <h3>Error message (visible to admin only)</h3>
  87. <p><strong>#{CGI.escapeHTML exception.message}</strong></p>
  88. <div>#{exception.backtrace * "<br>\n"}</div>
  89. }
  90. end
  91. 1 def backtrace_link cardname, exception
  92. # TODO: make this a modal link after new modal handling is merged in
  93. wrap_with(:span, title: error_message(exception)) { cardname }
  94. end
  95. 1 def standard_errors heading=nil
  96. 1 alert "warning", true do
  97. [
  98. 1 (wrap_with(:h4, heading, class: "alert-heading error") if heading),
  99. error_messages.join("<hr>")
  100. ]
  101. end
  102. end
  103. 1 def error_messages
  104. 1 card.errors.map do |attrib, msg|
  105. 1 attrib == :abort ? h(msg) : standard_error_message(attrib, msg)
  106. end
  107. end
  108. 1 def standard_error_message attribute, message
  109. 1 "<p><strong>#{h attribute.to_s.upcase}:</strong> #{h message}</p>"
  110. end
  111. 1 def not_found_errors
  112. if card.errors.any?
  113. standard_errors
  114. else
  115. haml :not_found
  116. end
  117. end
  118. 1 def sign_in_or_up_links to_task
  119. return if Auth.signed_in?
  120. links = [signin_link, signup_link].compact.join " #{tr :or} "
  121. wrap_with(:div) do
  122. [tr(:please), links, to_task].join(" ") + "."
  123. end
  124. end
  125. 1 def signin_link
  126. link_to_card :signin, tr(:sign_in),
  127. class: "signin-link", slotter: true, path: { view: :open }
  128. end
  129. 1 def signup_link
  130. return unless signup_ok?
  131. link_to_card :signup, tr(:sign_up),
  132. class: "signup-link", slotter: true, path: { action: :new }
  133. end
  134. 1 def signup_ok?
  135. Card.new(type_id: Card::SignupID).ok? :create
  136. end
  137. 1 def quiet_denial
  138. wrap_with :span, class: "denied" do
  139. "<!-- Sorry, you don't have permission (#{@denied_task}) -->"
  140. end
  141. end
  142. 1 def loud_denial
  143. voo.hide :menu
  144. frame do
  145. [wrap_with(:h1, tr(:sorry)),
  146. wrap_with(:div, loud_denial_message)]
  147. end
  148. end
  149. 1 def loud_denial_message
  150. to_task = @denied_task ? tr(:denied_task, denied_task: @denied_task) : tr(:to_do_that)
  151. case
  152. when not_denied_task_read?
  153. tr(:read_only)
  154. when Auth.signed_in?
  155. tr(:need_permission_task, task: to_task)
  156. else
  157. Env.save_interrupted_action request.env["REQUEST_URI"]
  158. sign_in_or_up_links to_do_unauthorized_task
  159. end
  160. end
  161. 1 def not_denied_task_read?
  162. @denied_task != :read && Card.config.read_only
  163. end
  164. 1 def to_do_unauthorized_task
  165. @denied_task ? tr(:denied_task, denied_task: @denied_task) : tr(:to_do_that)
  166. end
  167. end
  168. end;end;end;end;end;
  169. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/error.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/frame.rb

96.97% lines covered

33 relevant lines. 32 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Frame)
  4. #
  5. 1 module Frame;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/frame.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :flash, cache: :never, unknown: true, perms: :none do
  10. 300 flash_notice = params[:flash] || Env.success.flash
  11. 300 return "" unless flash_notice.present? && focal?
  12. Array(flash_notice).join "\n"
  13. end
  14. 1 def frame &block
  15. 52 standard_frame &block
  16. end
  17. 1 def standard_frame slot=true
  18. 52 with_frame slot do
  19. 104 wrap_body { yield } if block_given?
  20. end
  21. end
  22. 1 def with_frame slot=true, header=frame_header, slot_opts={}
  23. 52 voo.hide :help
  24. 52 add_name_context
  25. 52 wrap slot, slot_opts do
  26. 52 panel do
  27. 52 [header, frame_help, render_flash, (yield if block_given?)]
  28. end
  29. end
  30. end
  31. 1 def frame_header
  32. 52 _render_header
  33. end
  34. 1 def frame_help
  35. 74 with_class_up "help-text", "alert alert-info" do
  36. 74 _render :help
  37. end
  38. end
  39. 1 def frame_and_form action, form_opts={}
  40. 51 form_opts ||= {}
  41. 51 frame do
  42. 51 card_form action, form_opts do
  43. 51 yield
  44. end
  45. end
  46. end
  47. 1 def panel
  48. 52 wrap_with :div, class: classy("d0-card-frame") do
  49. 52 yield
  50. end
  51. end
  52. end
  53. end;end;end;end;end;
  54. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/frame.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/header.rb

77.78% lines covered

36 relevant lines. 28 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Header)
  4. #
  5. 1 module Header;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/header.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :header, perms: :none do
  10. 300 main_header
  11. end
  12. 1 def main_header
  13. 300 header_wrap _render_header_title
  14. end
  15. 1 def header_wrap content=nil
  16. 300 haml :header_wrap, content: (block_given? ? yield : output(content))
  17. end
  18. 1 view :header_title, perms: :none do
  19. 300 header_title_elements
  20. end
  21. 1 def header_title_elements
  22. 300 voo.hide :title_toggle if show_view?(:icon_toggle, :hide)
  23. 300 title_view = show_view?(:title_toggle, :hide) ? :title_toggle : :title
  24. 300 [_render_icon_toggle(optional: :hide), _render(title_view)]
  25. end
  26. 1 def show_draft_link?
  27. card.drafts.present? && @slot_view == :edit
  28. end
  29. 1 view :title_toggle, perms: :none do
  30. content_toggle(_render_title(hide: :title_link))
  31. end
  32. 1 view :icon_toggle, perms: :none do
  33. direction = @toggle_mode == :close ? :expand : :collapse_down
  34. content_toggle icon_tag(direction)
  35. end
  36. 1 view :toggle, :icon_toggle # deprecated; use icon_toggle
  37. 1 def content_toggle text=""
  38. return if text.nil?
  39. verb, adjective = toggle_verb_adjective
  40. link_to_view adjective, text, title: "#{verb} #{card.name}", # LOCALIZE
  41. class: "toggle-#{adjective} toggler nodblclick"
  42. end
  43. 1 def toggle_view
  44. 300 toggle_verb_adjective.last
  45. end
  46. 1 TOGGLE_MAP = { close: %w[open open], open: %w[close closed] } # LOCALIZE first item
  47. 1 def toggle_verb_adjective
  48. 300 TOGGLE_MAP[@toggle_mode || :open] ||
  49. raise(Card::Error, "invalid toggle mode: #{@toggle_mode}")
  50. end
  51. 1 def structure_editable?
  52. card.structure && card.template.ok?(:update)
  53. end
  54. end
  55. end;end;end;end;end;
  56. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/header.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/html_views/guide.rb

81.82% lines covered

33 relevant lines. 27 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 5 class Card; module Set; class All; module RichHtml;; module HtmlViews;
  3. # Set: All cards (RichHtml, HtmlViews, Guide)
  4. #
  5. 1 module Guide;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/guide.rb"; end
  8. 1 def guide_card
  9. 100 guide_card = rule_card(:guide)
  10. 100 return unless guide_card
  11. 100 guide_card = guide_card.first_card if guide_card.type_id == Card::PointerID
  12. 100 guide_card if guide_card.ok?(:read)
  13. end
  14. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  15. 1 view :guide, unknown: true, cache: :never, wrap: :slot do
  16. guide
  17. end
  18. 1 def guide
  19. 50 guide_text = rule_based_guide
  20. 50 return "" unless guide_text.present?
  21. 50 if (rule_card = card.help_rule_card)
  22. edit_link = with_nest_mode(:normal) { nest(rule_card, view: :edit_link) }
  23. guide_text = "<span class='d-none'>#{edit_link}</span>#{guide_text}"
  24. end
  25. 50 wrap_with :div, guide_text, class: classy("guide-text")
  26. end
  27. 1 def alert_guide
  28. 50 guide_text = guide
  29. 50 return "" unless guide_text.present?
  30. 100 alert(:secondary, true, false, class: "guide") { guide_text }
  31. end
  32. 1 def raw_guide_text
  33. 50 false
  34. end
  35. 1 def rule_based_guide
  36. 50 if raw_guide_text
  37. with_nest_mode :normal do
  38. process_content raw_guide_text, chunk_list: :references
  39. # render guide text with current card's format
  40. # so current card's context is used in guide card nests
  41. end
  42. 50 elsif card.guide_card
  43. 50 with_nest_mode :normal do
  44. 50 nest card.guide_card, view: :core
  45. end
  46. else
  47. ""
  48. end
  49. end
  50. end
  51. end;end;end;end;end;end;
  52. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/guide.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/html_views/help.rb

93.55% lines covered

31 relevant lines. 29 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 5 class Card; module Set; class All; module RichHtml;; module HtmlViews;
  3. # Set: All cards (RichHtml, HtmlViews, Help)
  4. #
  5. 1 module Help;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/help.rb"; end
  8. 1 def help_rule_card
  9. 831 help_card = rule_card(:help)
  10. 831 help_card if help_card&.ok?(:read)
  11. end
  12. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  13. 1 view :help, unknown: true, cache: :never, wrap: :slot do
  14. 286 help = help_text
  15. 286 return "" unless help.present?
  16. 14 wrap_with :div, wrap_help_text(help), class: classy("help-text")
  17. end
  18. 1 view :help_text, unknown: true, cache: :never do
  19. 236 wrap_help_text help_text
  20. end
  21. 1 def wrap_help_text text
  22. 269 help = text
  23. 269 if (rule_card = card.help_rule_card)
  24. 4 edit_link = with_nest_mode(:normal) { nest(rule_card, view: :edit_link) }
  25. 2 help = "<span class='d-none'>#{edit_link}</span>#{text}"
  26. end
  27. 269 help
  28. end
  29. 1 view :lead do
  30. class_up "card-slot", "lead"
  31. _view_content
  32. end
  33. 1 def help_text
  34. 522 voo.help || rule_based_help
  35. end
  36. 1 def raw_help_text
  37. 534 card.try(:raw_help_text) || card.help_rule_card&.content
  38. end
  39. 1 def rule_based_help
  40. 540 return "" unless (help_text = raw_help_text)
  41. 30 with_nest_mode :normal do
  42. 30 process_content help_text, chunk_list: :references
  43. # render help card with current card's format
  44. # so current card's context is used in help card nests
  45. end
  46. end
  47. end
  48. end;end;end;end;end;end;
  49. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/help.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/html_views/info.rb

38.1% lines covered

42 relevant lines. 16 lines covered and 26 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 5 class Card; module Set; class All; module RichHtml;; module HtmlViews;
  3. # Set: All cards (RichHtml, HtmlViews, Info)
  4. #
  5. 1 module Info;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/info.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :type, unknown: true do
  10. 248 link_to_card card.type_card, nil, class: "cardtype"
  11. end
  12. 1 view :change do
  13. voo.show :title_link
  14. voo.hide :menu
  15. wrap do
  16. [_render_title,
  17. _render_menu,
  18. _render_last_action]
  19. end
  20. end
  21. 1 view :last_action do
  22. act = card.last_act
  23. return unless act
  24. action = act.action_on card.id
  25. return unless action
  26. action_verb =
  27. case action.action_type
  28. when :create then "added"
  29. when :delete then "deleted"
  30. else
  31. link_to_view :history, "edited", class: "last-edited", rel: "nofollow"
  32. end
  33. %(
  34. <span class="last-update">
  35. #{action_verb} #{_render_acted_at} ago by
  36. #{subformat(card.last_actor)._render_link}
  37. </span>
  38. )
  39. end
  40. 1 view :type_info do
  41. return unless card.type_code != :basic
  42. wrap_with :span, class: "type-info float-right" do
  43. link_to_card card.type_name, nil, class: "navbar-link"
  44. end
  45. end
  46. 1 view :view_list do
  47. %i[bar box info_bar open closed titled labeled content content_panel].map do |v|
  48. wrap_with :p, [content_tag(:h3, v), render(v, show: :menu)]
  49. end.flatten.join ""
  50. end
  51. 1 view :demo do
  52. frame do
  53. [
  54. view_select,
  55. wrap_with(:div, view_demo, class: "demo-slot")
  56. ]
  57. end
  58. end
  59. 1 def demo_view
  60. Env.params[:demo_view] || :core
  61. end
  62. 1 def view_demo
  63. wrap(true) do
  64. render demo_view
  65. end
  66. end
  67. 1 def view_select
  68. card_form :get, success: { view: :demo } do
  69. select_tag :demo_view, options_for_select(all_views, demo_view),
  70. class: "_submit-on-select"
  71. end
  72. end
  73. 1 def all_views
  74. Card::Set::Format::AbstractFormat::ViewDefinition.views
  75. .slice(*self.class.ancestors)
  76. .values.map(&:keys).flatten.uniq
  77. end
  78. end
  79. end;end;end;end;end;end;
  80. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/info.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/html_views/size.rb

61.54% lines covered

13 relevant lines. 8 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 5 class Card; module Set; class All; module RichHtml;; module HtmlViews;
  3. # Set: All cards (RichHtml, HtmlViews, Size)
  4. #
  5. 1 module Size;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/size.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 SIZE_IN_PX = { icon: 16, small: 75, medium: 200, large: 500 }.freeze
  10. # used to control size of svg
  11. 1 view :max_size do
  12. if voo.size.is_a?(String) && voo.size.match(/^\d+x\d+\$/)
  13. max_size(*voo.size.split("x"))
  14. else
  15. px = SIZE_IN_PX[voo.size&.to_sym] || 200
  16. max_size px, px
  17. end
  18. end
  19. 1 def max_size w, h
  20. "max-width: #{w}px; max-height: #{h}px"
  21. end
  22. end
  23. end;end;end;end;end;end;
  24. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/html_views/size.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/menu.rb

87.14% lines covered

70 relevant lines. 61 lines covered and 9 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Menu)
  4. #
  5. 1 module Menu;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/menu.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :menu, denial: :blank, unknown: true do
  10. 305 return "" if card.unknown?
  11. 255 wrap_with :div, class: "card-menu #{menu_link_classes}" do
  12. 255 [render_help_link,
  13. menu_link,
  14. 255 (voo.show?(:bridge_link) ? bridge_link(false) : nil)]
  15. end
  16. end
  17. 1 def menu_link
  18. 255 case voo.edit
  19. when :inline
  20. edit_inline_link
  21. when :full
  22. edit_in_bridge_link
  23. else # :standard
  24. 255 edit_link
  25. end
  26. end
  27. 1 def edit_view
  28. case voo.edit
  29. when :inline
  30. :edit_inline
  31. when :full
  32. :edit
  33. else # :standard
  34. edit_link
  35. end
  36. end
  37. 1 view :edit_link, unknown: true, denial: :blank do
  38. 2 edit_link edit_link_view
  39. end
  40. 1 def edit_link_view
  41. :edit
  42. end
  43. 1 view :full_page_link do
  44. full_page_link
  45. end
  46. 1 view :bridge_link, unknown: true do
  47. 28 bridge_link
  48. end
  49. 1 def bridge_link in_modal=true
  50. 283 opts = { class: "bridge-link" }
  51. 283 if in_modal
  52. # add_class opts, "close"
  53. 28 opts["data-slotter-mode"] = "modal-replace"
  54. end
  55. 283 link_to_view :bridge, material_icon(:more_horiz), opts
  56. end
  57. # no caching because help_text view doesn't cache, and we can't have a
  58. # stub in the data-content attribute or it will get html escaped.
  59. 1 view :help_link, cache: :never, unknown: true do
  60. 255 help_link render_help_text, help_title
  61. end
  62. 1 def help_link text=nil, title=nil
  63. 255 opts = help_popover_opts text, title
  64. 255 add_class opts, "_card-menu-popover"
  65. 255 link_to help_icon, opts
  66. end
  67. 1 def help_popover_opts text=nil, title=nil
  68. 255 text ||= render_help_text
  69. 255 opts = { "data-placement": :left, class: "help-link" }
  70. 255 popover_opts text, title, opts
  71. end
  72. 1 def help_icon
  73. 255 material_icon("help")
  74. end
  75. 1 def help_title
  76. 255 "#{name_parts_links} (#{render_type}) #{full_page_link unless card.simple?}"
  77. end
  78. 1 def name_parts_links
  79. 255 card.name.parts.map do |part|
  80. 391 link_to_card part
  81. end.join Card::Name.joint
  82. end
  83. 1 def full_page_link
  84. 109 link_to_card full_page_card, full_page_icon, class: classy("full-page-link")
  85. end
  86. 1 def full_page_card
  87. 109 card
  88. end
  89. 1 def edit_in_bridge_link opts={}
  90. edit_link :bridge, opts
  91. end
  92. 1 def edit_link view=:edit, opts={}
  93. 257 link_to_view view, opts.delete(:link_text) || menu_icon,
  94. edit_link_opts(opts.reverse_merge(modal: :lg))
  95. end
  96. # @param modal [Symbol] modal size
  97. 1 def edit_link_opts modal: nil
  98. 257 opts = { class: classy("edit-link") }
  99. 257 if modal
  100. 257 opts[:"data-slotter-mode"] = "modal"
  101. 257 opts[:"data-modal-class"] = "modal-#{modal}"
  102. end
  103. 257 opts
  104. end
  105. 1 def menu_link_classes
  106. 255 "nodblclick" + (show_view?(:hover_link) ? " _show-on-hover" : "")
  107. end
  108. 1 def menu_icon
  109. 279 material_icon "edit"
  110. end
  111. 1 def full_page_icon
  112. 109 icon_tag :open_in_new
  113. end
  114. end
  115. end;end;end;end;end;
  116. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/menu.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/modal.rb

80.0% lines covered

65 relevant lines. 52 lines covered and 13 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Modal)
  4. #
  5. 1 module Modal;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/modal.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 MODAL_SIZE = { small: "sm", medium: nil, large: "lg", full: "full" }.freeze
  10. 1 MODAL_CLOSE_OPTS = { "data-dismiss": "modal",
  11. "data-cy": "close-modal" }.freeze
  12. 1 wrapper :modal do |opts={}|
  13. 22 haml :modal_dialog, body: interior,
  14. classes: modal_dialog_classes(opts),
  15. title: normalize_modal_option(:title, opts),
  16. menu: normalize_modal_option(:menu, opts),
  17. footer: normalize_modal_option(:footer, opts)
  18. end
  19. 1 def normalize_modal_option key, opts
  20. 66 val = opts[key]
  21. 66 return render("modal_#{key}") unless val
  22. 66 cast_model_option val
  23. end
  24. 1 def cast_model_option val
  25. 95 case val
  26. when Symbol
  27. 66 cast_model_option_symbol val
  28. when Proc
  29. val.call(self)
  30. else
  31. 29 val
  32. end
  33. end
  34. 1 def cast_model_option_symbol val
  35. 66 respond_to?(val) ? send(val) : val
  36. end
  37. 1 view :modal, wrap: :modal do
  38. ""
  39. end
  40. 1 def show_in_modal_link link_text, body
  41. link_to_view :modal, link_text, "data-modal-body": body, "data-slotter-mode": "modal"
  42. end
  43. 1 def modal_close_button link_text="Close", opts={}
  44. 21 classes = opts.delete(:class)
  45. 21 button_opts = opts.merge(MODAL_CLOSE_OPTS)
  46. 21 add_class button_opts, classes if classes
  47. 21 button_tag link_text, button_opts
  48. end
  49. 1 def modal_submit_button opts={}
  50. add_class opts, "submit-button _close-modal"
  51. submit_button opts
  52. end
  53. 1 view :modal_menu, unknown: true, wrap: :modal_menu do
  54. [close_modal_window, pop_out_modal_window]
  55. end
  56. 1 wrapper :modal_menu, :div, class: "modal-menu ml-auto"
  57. 1 view :modal_title, unknown: true do
  58. ""
  59. end
  60. 1 view :modal_footer, unknown: true do
  61. button_tag "Close",
  62. class: "btn-xs _close-modal float-right",
  63. "data-dismiss" => "modal"
  64. end
  65. 1 view :modal_link do
  66. modal_link _render_title, size: voo.size
  67. end
  68. 1 def modal_link text=nil, opts={}
  69. opts = modal_link_opts(opts)
  70. opts[:path][:layout] ||= :modal
  71. link_to text, opts
  72. end
  73. 1 def modal_link_opts opts
  74. 7 add_class opts, "slotter"
  75. 7 opts.reverse_merge! path: {},
  76. "data-slotter-mode": "modal",
  77. "data-modal-class": modal_dialog_classes(opts),
  78. remote: true
  79. 7 opts
  80. end
  81. 1 def modal_dialog_classes opts
  82. 29 classes = [classy("modal-dialog")]
  83. 29 return classes unless opts.present?
  84. 29 add_modal_size_class classes, opts[:size]
  85. 29 classes << "modal-dialog-centered" if opts[:vertically_centered]
  86. 29 classes.join " "
  87. end
  88. 1 def add_modal_size_class classes, size
  89. 29 size = normalize_modal_size_class size
  90. 29 return if size == :medium || size.blank?
  91. 22 classes << "modal-#{MODAL_SIZE[size]}"
  92. end
  93. 1 def normalize_modal_size_class size
  94. 29 size.in?(MODAL_SIZE.keys) ? size : cast_model_option(size)
  95. end
  96. 1 def close_modal_window
  97. 22 link_to icon_tag(:close), path: "",
  98. class: "_close-modal close",
  99. "data-dismiss": "modal"
  100. end
  101. 1 def pop_out_modal_window
  102. link_to icon_tag(:new_window), path: {}, class: "pop-out-modal close"
  103. end
  104. end
  105. end;end;end;end;end;
  106. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/modal.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/overlay.rb

37.1% lines covered

62 relevant lines. 23 lines covered and 39 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Overlay)
  4. #
  5. 1 module Overlay;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/overlay.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 OVERLAY_CLOSE_OPTS = { class: "_close-overlay btn-sm",
  10. "data-dismiss": "overlay",
  11. type: "button" }.freeze
  12. 1 wrapper :overlay do |opts|
  13. class_up "card-slot", "_overlay d0-card-overlay bg-body"
  14. @content_body = true
  15. voo.hide! :menu
  16. overlay_frame true, overlay_header(opts[:title]), opts[:slot] do
  17. interior
  18. end
  19. end
  20. 1 view :overlay_header, unknown: true do
  21. overlay_header
  22. end
  23. 1 view :overlay_title do
  24. _render_title
  25. end
  26. 1 view :overlay_menu do
  27. wrap_with :div, class: "btn-group btn-group-sm align-self-start ml-auto" do
  28. [render_overlay_help_link, slotify_overlay_link, close_overlay_link]
  29. end
  30. end
  31. 1 view :overlay_help_link, cache: :never, unknown: true do
  32. opts = help_popover_opts
  33. add_open_guide_opts opts
  34. overlay_menu_link "question-circle", opts
  35. end
  36. 1 def add_open_guide_opts opts
  37. return unless card.guide_card
  38. slot_selector = ".bridge-sidebar > ._overlay-container-placeholder > .card-slot"
  39. opts.merge! remote: true,
  40. href: path(mark: card, view: :overlay_guide),
  41. "data-slot-selector": slot_selector,
  42. "data-slotter-mode": "overlay"
  43. add_class opts, "slotter"
  44. end
  45. 1 def slotify_overlay_link
  46. overlay_menu_link "external-link-square", card: card
  47. end
  48. 1 def close_overlay_link
  49. overlay_menu_link :close, path: "#", "data-dismiss": "overlay"
  50. end
  51. 1 def overlay_close_button link_text="Close", opts={}
  52. classes = opts.delete(:class)
  53. button_opts = opts.merge(OVERLAY_CLOSE_OPTS)
  54. add_class button_opts, classes if classes
  55. button_tag link_text, button_opts
  56. end
  57. 1 def overlay_delete_button
  58. opts = { no_success: true }.merge OVERLAY_CLOSE_OPTS
  59. delete_button opts
  60. end
  61. 1 def overlay_save_and_close_button
  62. submit_button text: "Save and Close", class: "_close-on-success",
  63. "data-cy": "submit-overlay"
  64. end
  65. 1 def overlay_menu_link icon, args={}
  66. add_class args, "border-light text-dark p-1 ml-1"
  67. button_link fa_icon(icon, class: "fa-lg"), args.merge(btn_type: "outline-secondary")
  68. end
  69. 1 def overlay_header title=nil
  70. title ||= _render_overlay_title
  71. class_up "d0-card-header", "bg-body"
  72. class_up "d0-card-header-title", "d-flex"
  73. header_wrap [title, _render_overlay_menu]
  74. end
  75. 1 def overlay_frame slot=true, header=render_overlay_header, slot_opts=nil
  76. slot_opts ||= {}
  77. overlay_framer slot, header, slot_opts do
  78. wrap_body { yield }
  79. end
  80. end
  81. 1 def haml_overlay_frame slot=true, header=render_overlay_header
  82. overlay_framer slot, header, {} do
  83. haml_wrap_body { yield }
  84. end
  85. end
  86. 1 private
  87. 1 def overlay_framer slot, header, slot_opts
  88. class_up "card-slot", "_overlay"
  89. with_frame slot, header, slot_opts do
  90. yield
  91. end
  92. end
  93. end
  94. end;end;end;end;end;
  95. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/overlay.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/process_layout.rb

93.02% lines covered

43 relevant lines. 40 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, ProcessLayout)
  4. #
  5. 1 module ProcessLayout;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/process_layout.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # TODO: use CodeFile cards for these
  10. # builtin layouts allow for rescue / testing
  11. # HTML_LAYOUTS = Mod::Loader.load_layouts(:html).merge "none" => "{{_main}}"
  12. # HAML_LAYOUTS = Mod::Loader.load_layouts(:haml)
  13. 1 def show_with_page_layout view, args
  14. 224 main!
  15. 224 args = main_render_args view, args
  16. 224 if explicit_modal_wrapper?(view) && page_layout.to_sym != :modal
  17. 21 render_outside_of_layout view, args
  18. else
  19. 203 render_with_layout view, page_layout, args
  20. end
  21. end
  22. 1 def main_render_args view, args
  23. 224 args[:view] = view if view
  24. 224 args[:main] = true
  25. 224 args[:main_view] = true
  26. 224 args
  27. end
  28. 1 def page_layout
  29. 245 params[:layout] || layout_name_from_rule || :default
  30. end
  31. 1 def render_with_layout view, layout, args={}
  32. 224 view_opts = Layout.main_nest_opts(layout, self)
  33. 224 view ||= view_opts.delete(:view) || default_nest_view
  34. 224 view_opts[:home_view] = view
  35. 224 view_opts[:layout] = layout
  36. 224 render! view, view_opts.reverse_merge(args)
  37. end
  38. 1 def render_outside_of_layout view, args
  39. 21 body = render_with_layout(nil, page_layout, {})
  40. 21 modal = render!(view, args)
  41. 21 if body.include?("</body>")
  42. # a bit hacky
  43. # the problem is that the body tag has to be in the layout
  44. # so that you can add layout css classes like <body class="right-sidebar">
  45. 21 body.sub!("</body>", "#{modal}</body>")
  46. else
  47. body += modal
  48. end
  49. 21 body
  50. end
  51. 1 def show_layout?
  52. 845 !Env.ajax? || params[:layout]
  53. end
  54. 1 def explicit_modal_wrapper? view
  55. 224 return unless view_setting(:wrap, view)
  56. 42 wrapper_names(view_setting(:wrap, view)).any? { |n| n == :modal || n == :bridge }
  57. end
  58. 1 def wrapper_names wrappers
  59. 21 case wrappers
  60. 21 when Hash then wrappers.keys
  61. when Array then wrappers.map { |w| w.is_a?(Array) ? w.first : w }
  62. else [wrappers]
  63. end
  64. end
  65. 1 def layout_name_from_rule
  66. 245 card.rule_card(:layout)&.try :item_name
  67. end
  68. end
  69. end;end;end;end;end;
  70. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/process_layout.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/show.rb

84.0% lines covered

25 relevant lines. 21 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Show)
  4. #
  5. 1 module Show;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/show.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def show view, args
  10. 274 content = send show_method, view, args
  11. 274 show_full_page? ? wrap_with_html_page(content) : content
  12. # TODO: remove the following after tracking down wikirate encoding bug
  13. rescue Card::Error::ServerError => e
  14. if e.message.match?(/invalid byte sequence/)
  15. Card::Lexicon.cache.reset
  16. Rails.logger.info "reset name cache to prevent encoding freakiness"
  17. end
  18. raise e
  19. end
  20. 1 def show_method
  21. 274 "show_#{show_layout? ? :with : :without}_page_layout"
  22. end
  23. 1 def show_without_page_layout view, args
  24. 50 @main = true if params[:is_main] || args[:main]
  25. 50 args.delete(:layout)
  26. 50 view ||= args[:home_view] || :open # default_nest_view
  27. 50 render! view, args
  28. end
  29. 1 def show_full_page?
  30. 274 !Env.ajax?
  31. end
  32. 1 wrapper :html_page do
  33. 224 <<-HTML.strip_heredoc
  34. <!DOCTYPE HTML>
  35. <html class="h-100">
  36. <head>
  37. #{head_content}
  38. </head>
  39. #{interior}
  40. </html>
  41. HTML
  42. end
  43. 1 def head_content
  44. 224 nest card.rule_card(:head), view: :head_content
  45. end
  46. end
  47. end;end;end;end;end;
  48. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/show.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/title.rb

89.29% lines covered

28 relevant lines. 25 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Title)
  4. #
  5. 1 module Title;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/title.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 view :title, compact: true, perms: :none do
  10. standard_title
  11. end
  12. 1 def standard_title
  13. 1683 name_variant title_in_context(voo.title)
  14. end
  15. end
  16. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  17. 1 view :title do
  18. 1683 show_view?(:title_link, :hide) ? render_title_link : render_title_no_link
  19. end
  20. 1 view :title_link, compact: true, perms: :none do
  21. link_to_card card.name, render_title_no_link
  22. end
  23. 1 view :title_no_link, compact: true, perms: :none do
  24. 1683 wrapped_title standard_title
  25. end
  26. 1 def title_with_link link_text
  27. link_to_card card.name, link_text
  28. end
  29. 1 def safe_name
  30. 253 h super
  31. end
  32. 1 def title_in_context title=nil
  33. 1686 title = title&.html_safe
  34. # escape titles generated from card names, but not those set explicitly
  35. 1686 h super(title)
  36. end
  37. 1 def wrapped_title title
  38. 1683 wrap_with :span, class: classy("card-title"), title: title do
  39. 1683 title.to_name.parts.join wrapped_joint
  40. end
  41. end
  42. 1 def wrapped_joint
  43. 1683 wrap_with :span, "+", classy("joint")
  44. end
  45. end
  46. end;end;end;end;end;
  47. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/title.rb ~~

card/tmpsets/set/mod010-standard/all/rich_html/wrapper.rb

86.59% lines covered

82 relevant lines. 71 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module RichHtml;
  3. # Set: All cards (RichHtml, Wrapper)
  4. #
  5. 1 module Wrapper;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/wrapper.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # Does two main things:
  10. # (1) gives CSS classes for styling and
  11. # (2) adds card data for javascript - including the "card-slot" class,
  12. # which in principle is not supposed to be in styles
  13. 1 def wrap slot=true, slot_attr={}, tag=:div, &block
  14. 702 method_wrap :wrap_with, tag, slot, slot_attr, &block
  15. end
  16. 1 wrapper :slot do |opts|
  17. 498 class_up "card-slot", opts[:class] if opts[:class]
  18. 498 method_wrap :wrap_with, :div, true, opts do
  19. 498 interior
  20. end
  21. end
  22. 1 def haml_wrap slot=true, slot_attr={}, tag=:div, &block
  23. method_wrap :haml_tag, tag, slot, slot_attr, &block
  24. end
  25. 1 def method_wrap method, tag, slot, slot_attr, &block
  26. 1200 @slot_view = @current_view
  27. 1200 debug_slot do
  28. 1200 send method, tag, slot_attributes(slot, slot_attr), &block
  29. end
  30. end
  31. 1 def slot_attributes slot, slot_attr
  32. 1200 { id: slot_id, class: wrap_classes(slot), data: wrap_data }.tap do |hash|
  33. 1200 add_class hash, slot_attr.delete(:class)
  34. 1200 hash.deep_merge! slot_attr
  35. end
  36. end
  37. 1 def slot_id
  38. 1200 "#{card.name.safe_key}-#{@current_view}-view"
  39. end
  40. 1 def wrap_data slot=true
  41. 1200 with_slot_data slot do
  42. 1200 { "card-id": card.id, "card-name": h(card.name),
  43. "slot-id": SecureRandom.hex(10) }
  44. end
  45. end
  46. 1 def with_slot_data slot
  47. 1200 hash = yield
  48. # rails helper convert slot hash to json
  49. # but haml joins nested keys with a dash
  50. 1200 hash[:slot] = slot_options_json if slot
  51. 1200 hash
  52. end
  53. 1 def slot_options_json
  54. 1200 html_escape_except_quotes JSON(slot_options)
  55. end
  56. 1 def slot_options
  57. 1200 options = voo ? voo.slot_options : {}
  58. 1200 name_context_slot_option options
  59. 1200 options
  60. end
  61. 1 def name_context_slot_option opts
  62. 1200 return unless initial_context_names.present?
  63. 311 opts[:name_context] = initial_context_names.map(&:key) * ","
  64. end
  65. 1 def debug_slot
  66. 1200 debug_slot? ? debug_slot_wrap { yield } : yield
  67. end
  68. 1 def debug_slot?
  69. 1200 params[:debug] == "slot"
  70. end
  71. 1 def debug_slot_wrap
  72. pre = "<!--\n\n#{' ' * depth}"
  73. post = " SLOT: #{h card.name}\n\n-->"
  74. [pre, "BEGIN", post, yield, pre, "END", post].join
  75. end
  76. 1 def wrap_classes slot
  77. 1200 list = slot ? ["card-slot"] : []
  78. 1200 list += ["#{@current_view}-view", card.safe_set_keys]
  79. 1200 list << "STRUCTURE-#{voo.structure.to_name.key}" if voo&.structure
  80. 1200 classy list
  81. end
  82. 1 def wrap_body
  83. 612 wrap_with(:div, class: body_css_classes) { yield }
  84. end
  85. 1 def haml_wrap_body
  86. wrap_body do
  87. capture_haml { yield }
  88. end
  89. end
  90. 1 def body_css_classes
  91. 306 css_classes = ["d0-card-body"]
  92. 306 css_classes += ["d0-card-content", card.safe_set_keys] if @content_body
  93. 306 classy(*css_classes)
  94. end
  95. 1 def wrap_main
  96. 224 return yield if no_main_wrap?
  97. 224 wrap_with :div, yield, id: "main"
  98. end
  99. 1 def no_main_wrap?
  100. 224 Env.ajax? || params[:layout] == "none"
  101. end
  102. 1 def wrap_with tag, content_or_args={}, html_args={}
  103. 16692 content = block_given? ? yield : content_or_args
  104. 16692 tag_args = block_given? ? content_or_args : html_args
  105. 33384 content_tag(tag, tag_args) { output(content).to_s.html_safe }
  106. end
  107. 1 def wrap_each_with tag, content_or_args={}, args={}
  108. content = block_given? ? yield(args) : content_or_args
  109. args = block_given? ? content_or_args : args
  110. content.compact.map do |item|
  111. wrap_with(tag, args) { item }
  112. end.join "\n"
  113. end
  114. 1 private
  115. 1 def html_escape_except_quotes string
  116. # to be used inside single quotes (makes for readable json attributes)
  117. 1200 string.to_s.gsub(/&/, "&amp;")
  118. .gsub(/\'/, "&apos;")
  119. .gsub(/>/, "&gt;")
  120. .gsub(/</, "&lt;")
  121. end
  122. 1 wrapper :div, :div
  123. 1 wrapper :em, :em
  124. 1 wrapper :none do
  125. interior
  126. end
  127. end
  128. end;end;end;end;end;
  129. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/all/rich_html/wrapper.rb ~~

card/tmpsets/set/mod010-standard/right/discussion.rb

60.0% lines covered

10 relevant lines. 6 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Discussion" cards
  4. #
  5. 1 module Discussion;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/discussion.rb"; end
  8. 1 view :titled, unknown: true do
  9. voo.show :comment_box
  10. super()
  11. end
  12. 1 view :open, unknown: true do
  13. voo.show :comment_box
  14. super()
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/discussion.rb ~~

card/tmpsets/set/mod010-standard/right/head.rb

84.62% lines covered

13 relevant lines. 11 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Head" cards
  4. #
  5. 1 module Head;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/head.rb"; end
  8. 1 def ok_to_read
  9. 224 true
  10. end
  11. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  12. 1 view :head_content do
  13. 224 process_content render_raw
  14. end
  15. 1 view :one_line_content do
  16. raw_one_line_content
  17. end
  18. 1 view :core do
  19. process_content ::CodeRay.scan(render_raw, :html).div
  20. end
  21. end
  22. end;end;end;end;
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/head.rb ~~

card/tmpsets/set/mod010-standard/right/type_plus_right.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+TypePlusRight" cards
  4. #
  5. 1 module TypePlusRight;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/type_plus_right.rb"; end
  8. 1 def related_sets _with_self=false
  9. [[name, Card::Set::TypePlusRight.label(name.left)],
  10. ["#{name[1]}+*right", Card::Set::Right.label(name[1])]]
  11. end
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/type_plus_right.rb ~~

card/tmpsets/set/mod010-standard/right/when_created.rb

71.43% lines covered

7 relevant lines. 5 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+WhenCreated" cards
  4. #
  5. 1 module WhenCreated;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/when_created.rb"; end
  8. 1 def content
  9. return "" unless left&.real?
  10. I18n.localize left.created_at, format: :card_dayofwk_min_tz
  11. end
  12. # view :core, :raw
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/when_created.rb ~~

card/tmpsets/set/mod010-standard/right/when_last_edited.rb

71.43% lines covered

7 relevant lines. 5 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+WhenLastEdited" cards
  4. #
  5. 1 module WhenLastEdited;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/right/when_last_edited.rb"; end
  8. 1 def content
  9. return "" unless left&.real?
  10. I18n.localize left.updated_at, format: :card_dayofwk_min_tz
  11. end
  12. # view :core, :raw
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/right/when_last_edited.rb ~~

card/tmpsets/set/mod010-standard/self/alerts.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Alerts"
  4. #
  5. 1 module Alerts;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/alerts.rb"; end
  8. 1 def content
  9. "<!-- *alerts is deprecated. please remove from layout -->"
  10. end
  11. # view :core, :raw
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/alerts.rb ~~

card/tmpsets/set/mod010-standard/self/cardtype.rb

52.63% lines covered

19 relevant lines. 10 lines covered and 9 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Cardtype"
  4. #
  5. 1 module Cardtype;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/cardtype.rb"; end
  8. GROUP = {
  9. 1 "Text" => %w[RichText PlainText Markdown Phrase HTML],
  10. "Data" => %w[Number Toggle Date URI],
  11. "Upload" => %w[File Image],
  12. "Custom" => [],
  13. "Organize" => ["List", "Pointer", "Search", "Link list", "Nest list",
  14. "Mirror List", "Mirrored List"],
  15. "Template" => ["Notification template", "Email template", "Twitter template"],
  16. "Admin" => ["Cardtype", "User", "Role", "Sign up", "Session", "Set", "Setting"],
  17. "Styling" => ["Layout", "Skin", "Bootswatch skin", "Customized bootswatch skin", "CSS", "SCSS"],
  18. "Scripting" => %w[JSON JavaScript CoffeeScript]
  19. }.freeze
  20. #DEFAULT_RULE_GROUPS = ["Text", "Data", "Upload", "Organize - Search"]
  21. #STRUCTURE_RULE_GROUPS = ["Text", "Organize > Search"]
  22. # group for each cardtype: { "RichText => "Content", "Layout" => "Admin", ... }
  23. 1 GROUP_MAP = GROUP.each_with_object({}) do |(cat, types), h|
  24. 46 types.each { |t| h[t] = cat }
  25. end
  26. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  27. 1 view :grouped_list do
  28. GROUP.keys.map do |group|
  29. type_list = group == "Custom" ? custom_types : GROUP[group]
  30. next if type_list.empty?
  31. [wrap_with(:h5, group), wrap_with(:p, listing(type_list))]
  32. end.flatten.join "\n"
  33. end
  34. 1 def custom_types
  35. custom_types = []
  36. Card.search(type_id: Card::CardtypeID, return: "name").each do |name|
  37. next if ::Card::Set::Self::Cardtype::GROUP_MAP[name]
  38. custom_types << name
  39. end
  40. custom_types
  41. end
  42. end
  43. end;end;end;end;
  44. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/cardtype.rb ~~

card/tmpsets/set/mod010-standard/self/codenames.rb

100.0% lines covered

4 relevant lines. 4 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Codenames"
  4. #
  5. 1 module Codenames;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/codenames.rb"; end
  8. end;end;end;end;
  9. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/codenames.rb ~~

card/tmpsets/set/mod010-standard/self/foot.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Foot"
  4. #
  5. 1 module Foot;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/foot.rb"; end
  8. 1 def content
  9. "<!-- *foot is deprecated. please remove from layout -->"
  10. end
  11. # view :core, :raw
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/foot.rb ~~

card/tmpsets/set/mod010-standard/self/home.rb

88.89% lines covered

9 relevant lines. 8 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Home"
  4. #
  5. 1 module Home;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/home.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 view :home_url, perms: :none do
  10. card_url ""
  11. end
  12. 1 view :home_path, perms: :none do
  13. 448 card_path ""
  14. end
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/home.rb ~~

card/tmpsets/set/mod010-standard/self/now.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Now"
  4. #
  5. 1 module Now;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/now.rb"; end
  8. 1 def content
  9. I18n.localize(Time.now, format: :card_dayofwk_min_tz)
  10. end
  11. # view :core, :raw
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/now.rb ~~

card/tmpsets/set/mod010-standard/self/sidebar.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Sidebar"
  4. #
  5. 1 module Sidebar;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/self/sidebar.rb"; end
  8. 1 def raw_help_text
  9. <<-TEXT
  10. Sidebar content of [[Layouts]] with sidebar. [[http://decko.org/sidebar|more]]
  11. TEXT
  12. end
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/self/sidebar.rb ~~

card/tmpsets/set/mod010-standard/type/basic.rb

39.02% lines covered

41 relevant lines. 16 lines covered and 25 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Basic" cards
  4. #
  5. 1 module Basic;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/basic.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :open_content do
  10. with_table_of_contents _render_core
  11. end
  12. 1 view :titled_content do
  13. 175 with_table_of_contents _render_core
  14. end
  15. 1 def with_table_of_contents content
  16. 175 table_of_contents(content) || content
  17. end
  18. 1 def table_of_contents content
  19. 175 return if nest_mode == :compact || !content.present?
  20. 174 min = card.rule(:table_of_contents).to_i
  21. 174 return unless min && min > 0
  22. toc = toc_items content
  23. if toc.flatten.length >= min
  24. content.replace(
  25. %( <div class="table-of-contents"> <h5>#{tr(:toc)}</h5> ) +
  26. make_table_of_contents_list(toc) + "</div>" + content
  27. )
  28. end
  29. end
  30. 1 def toc_items content
  31. toc = []
  32. dep = 1
  33. content.gsub!(/<(h\d)>(.*?)<\/h\d>/i) do |match|
  34. if $LAST_MATCH_INFO
  35. tag, value = $LAST_MATCH_INFO[1, 2]
  36. value = strip_tags(value).strip
  37. next if value.empty?
  38. item = { value: value, uri: URI.escape(value) }
  39. case tag.downcase
  40. when "h1"
  41. item[:depth] = dep = 1
  42. toc << item
  43. when "h2"
  44. toc << [] if dep == 1
  45. item[:depth] = dep = 2
  46. toc.last << item
  47. end
  48. %(<a name="#{item[:uri]}"></a>#{match})
  49. end
  50. end
  51. toc
  52. end
  53. 1 def make_table_of_contents_list items
  54. list = items.map do |i|
  55. if i.is_a?(Array)
  56. make_table_of_contents_list(i)
  57. else
  58. %(<li><a href="##{i[:uri]}"> #{i[:value]}</a></li>)
  59. end
  60. end.join("\n")
  61. "<ol>" + list + "</ol>"
  62. end
  63. end
  64. end;end;end;end;
  65. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/basic.rb ~~

card/tmpsets/set/mod010-standard/type/cardtype.rb

71.62% lines covered

74 relevant lines. 53 lines covered and 21 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Cardtype" cards
  4. #
  5. 1 module Cardtype;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/cardtype.rb"; end
  8. 1 include_set Abstract::CqlSearch
  9. 1 def cql_content
  10. 2 { type_id: id, sort: :name }
  11. end
  12. 1 def related_sets with_self=false
  13. sets = []
  14. sets << ["#{name}+*type", Card::Set::Type.label(name)] if known?
  15. sets + super
  16. end
  17. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  18. 1 view :type, unknown: true do
  19. 7 link_to_card card.type_card, nil, class: "cardtype"
  20. end
  21. 1 def type_formgroup args={}
  22. if card.cards_of_type_exist?
  23. wrap_with :div, tr(:cards_exist, cardname: safe_name)
  24. else
  25. super
  26. end
  27. end
  28. 1 view :add_link do
  29. add_link
  30. end
  31. 1 view :add_button do
  32. 7 add_link class: "btn btn-secondary"
  33. end
  34. 1 def add_link opts={}
  35. 7 voo.title ||= tr(:add_card, cardname: safe_name)
  36. 7 link_to render_title, add_link_opts(opts)
  37. end
  38. 1 def add_link_opts opts
  39. 7 modal = opts.delete :modal
  40. 7 modal = true if modal.nil?
  41. 7 opts[:path] = add_path(modal ? :new_in_modal : :new)
  42. 7 modal ? modal_link_opts(opts) : opts
  43. end
  44. 1 view :add_url do
  45. card_url _render_add_path
  46. end
  47. 1 def add_path view
  48. 7 path_args = { mark: card.name }
  49. 7 process_voo_params(path_args) if voo.params
  50. 7 if view == :new
  51. path_args[:action] = :new
  52. else
  53. 7 path_args[:action] = :type
  54. 7 path_args[:view] = view
  55. end
  56. 7 path path_args
  57. end
  58. # don't cache because it depends on update permission for another card
  59. 1 view :configure_link, cache: :never, perms: ->(fmt) { fmt.can_configure? } do
  60. configure_link
  61. end
  62. 1 def can_configure?
  63. 7 Card.fetch(card, :type, :structure, new: {}).ok? :update
  64. end
  65. 1 view :configure_button, cache: :never, denial: :blank,
  66. 7 perms: ->(fmt) { fmt.can_configure? } do
  67. 7 configure_link "btn btn-secondary"
  68. end
  69. 1 def configure_link css_class=nil
  70. 7 return "" unless Card.fetch(card, :type, :structure, new: {}).ok? :update
  71. 7 voo.title ||= tr(:configure_card, cardname: safe_name.pluralize)
  72. 7 title = _render_title
  73. 7 link_to_card card, title, path: { view: :bridge, bridge: { tab: :rules_tab },
  74. set: Card::Name[safe_name, :type] },
  75. class: css_classes("configure-type-link ml-3", css_class)
  76. end
  77. 1 private
  78. 1 def process_voo_params path_args
  79. context = ((@parent&.card) || card).name
  80. Rack::Utils.parse_nested_query(voo.params).each do |key, value|
  81. value = value.to_name.absolute(context) if value
  82. key = key.to_name.absolute(context)
  83. path_args[key] = value
  84. end
  85. end
  86. end
  87. 1 include Basic
  88. 1 def cards_of_type_exist?
  89. !new_card? && Card.where(trash: false, type_id: id).exists?
  90. end
  91. 1 def create_ok?
  92. Card.new(type_id: id).ok? :create
  93. end
  94. 1 def was_cardtype?
  95. 7 type_id_before_act == Card::CardtypeID
  96. end
  97. 1 event :check_for_cards_of_type, after: :validate_delete do
  98. errors.add :cardtype, tr(:cards_exist, cardname: name) if cards_of_type_exist?
  99. end
  100. 1 event :check_for_cards_of_type_when_type_changed,
  101. :validate, changing: :type, when: :was_cardtype? do
  102. if cards_of_type_exist?
  103. errors.add :cardtype, tr(:error_cant_alter, name: name_before_act)
  104. end
  105. end
  106. 1 event :validate_cardtype_name, :validate, on: :save, changed: :name do
  107. 7 if %r{[<>/]}.match?(name)
  108. errors.add :name, tr(:error_invalid_character_in_cardtype, banned: "<, >, /")
  109. end
  110. end
  111. end;end;end;end;
  112. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/cardtype.rb ~~

card/tmpsets/set/mod010-standard/type/layout_type.rb

66.67% lines covered

12 relevant lines. 8 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "LayoutType" cards
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module LayoutType;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/layout_type.rb"; end
  9. 1 include_set Type::Html
  10. 1 event :update_layout_registry, :finalize, on: :update do
  11. Card::Layout.deregister_layout name
  12. Card::Layout.register_layout_with_nest name, format
  13. end
  14. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  15. 1 view :core do
  16. with_nest_mode :template do
  17. process_content ::CodeRay.scan(_render_raw, :html).div
  18. end
  19. end
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/layout_type.rb ~~

card/tmpsets/set/mod010-standard/type/notification_template.rb

60.87% lines covered

23 relevant lines. 14 lines covered and 9 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "NotificationTemplate" cards
  4. #
  5. 1 module NotificationTemplate;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/notification_template.rb"; end
  8. 1 card_reader :contextual_class
  9. 1 card_reader :disappear
  10. 1 card_reader :message
  11. 1 def deliver context
  12. success.flash alert_message(context)
  13. end
  14. 1 def alert_message context
  15. mcard = message.present? ? message_card : self
  16. format(:html).alert_message context, mcard
  17. end
  18. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  19. 1 def alert_message context, message_card
  20. mformat = subformat message_card
  21. alert card.alert_class, true, card.disappear? do
  22. mformat.contextual_content context, view: alert_view(mformat)
  23. end
  24. end
  25. 1 def alert_view format
  26. format.respond_to?(:notify) ? format.notify : :core
  27. end
  28. end
  29. 1 def disappear?
  30. disappear.present? ? disappear_card.checked? : true
  31. end
  32. 1 def alert_class
  33. contextual_class.present? ? contextual_class_card.item_name : :success
  34. end
  35. end;end;end;end;
  36. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/notification_template.rb ~~

card/tmpsets/set/mod010-standard/type/number.rb

53.33% lines covered

15 relevant lines. 8 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Number" cards
  4. #
  5. 1 module Number;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/number.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def input_type
  10. :text_field
  11. end
  12. end
  13. 1 event :validate_number, :validate, on: :save do
  14. errors.add :content, tr(:not_numeric, content: content) unless valid_number?(content)
  15. end
  16. 1 def valid_number? string
  17. return true if string.empty?
  18. valid = true
  19. begin
  20. Kernel.Float(string)
  21. rescue ArgumentError, TypeError
  22. valid = false
  23. end
  24. valid
  25. end
  26. end;end;end;end;
  27. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/number.rb ~~

card/tmpsets/set/mod010-standard/type/phrase.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Phrase" cards
  4. #
  5. 1 module Phrase;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/phrase.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def input_type
  10. 99 :text_field
  11. end
  12. end
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/phrase.rb ~~

card/tmpsets/set/mod010-standard/type/session.rb

69.44% lines covered

36 relevant lines. 25 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Session" cards
  4. #
  5. 1 module Session;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/session.rb"; end
  8. 1 include_set Pointer
  9. 1 def virtual?
  10. 164 session_content.present?
  11. end
  12. 1 def history?
  13. false
  14. end
  15. 1 def followable?
  16. false
  17. end
  18. 1 def recaptcha_on?
  19. 380 false
  20. end
  21. 1 def session_key
  22. 508 "_card_#{key}"
  23. end
  24. 1 def session_content
  25. 508 Env.session[session_key]
  26. end
  27. 1 def session_content= val
  28. Env.session[session_key] = val
  29. end
  30. 1 def content
  31. 970 db_content || session_content
  32. end
  33. 1 event :store_in_session, :prepare_to_store, on: :save do
  34. self.session_content = db_content
  35. abort :success
  36. end
  37. 1 event :delete_in_session, :prepare_to_store, on: :delete do
  38. self.session_content = nil
  39. abort :success
  40. end
  41. 1 def ok_to_create
  42. true
  43. end
  44. 1 def ok_to_update
  45. true
  46. end
  47. 1 def add_to_trash args
  48. yield args.merge trash: true
  49. end
  50. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  51. 1 before :core do
  52. voo.items[:view] = :name
  53. end
  54. end
  55. end;end;end;end;
  56. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/session.rb ~~

card/tmpsets/set/mod010-standard/type/toggle.rb

75.0% lines covered

24 relevant lines. 18 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Toggle" cards
  4. #
  5. 1 module Toggle;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/toggle.rb"; end
  8. 1 def checked?
  9. content == "1"
  10. end
  11. 1 view :core do
  12. 3 case card.content.to_i
  13. 1 when 1 then tr(:yes)
  14. 2 when 0 then tr(:no)
  15. else
  16. "?"
  17. end
  18. end
  19. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  20. 1 view :input do
  21. 1 toggle
  22. end
  23. 1 view :labeled_editor do
  24. toggle + toggle_label
  25. end
  26. 1 def toggle
  27. 1 check_box :content
  28. end
  29. 1 def toggle_label
  30. label :content, card.name.tag
  31. end
  32. 1 def one_line_content
  33. short_content
  34. end
  35. 1 def short_content
  36. render_core
  37. end
  38. end
  39. end;end;end;end;
  40. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/toggle.rb ~~

card/tmpsets/set/mod010-standard/type/uri.rb

75.0% lines covered

12 relevant lines. 9 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Uri" cards
  4. #
  5. 1 module Uri;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/standard/set/type/uri.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 view :core do
  10. link_to_resource _render_raw, render_title
  11. end
  12. 1 view :url_link do
  13. link_to_resource _render_raw
  14. end
  15. end
  16. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  17. 1 def input_type
  18. :text_field
  19. end
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/standard/set/type/uri.rb ~~

card/tmpsets/set/mod011-card-mod-email/abstract/email_field.rb

90.0% lines covered

20 relevant lines. 18 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (EmailField)
  4. #
  5. 1 module EmailField;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/abstract/email_field.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. # turn off autodetection of uri's
  10. 1 def chunk_list
  11. :references
  12. end
  13. end
  14. # format :html do
  15. # def pointer_items args
  16. # card.item_names(context: :raw).map do |iname|
  17. # wrap_item iname, args
  18. # end
  19. # end
  20. # end#
  21. 2 module EmailTextFormat; module_parent.send :register_set_format, Card::Format::EmailTextFormat, self; extend Card::Set::AbstractFormat
  22. 1 def email_addresses context
  23. 21 context ||= self
  24. 21 card.item_names(context: context.name).map do |name|
  25. # FIXME: context is processed twice here because pointers absolutize
  26. # item_names by default while other types can return relative names.
  27. # That's poor default behavior and should be fixed!
  28. 21 name = name.to_name.absolute context
  29. 21 email_address?(name) ? name : email_address_from_card(name, context)
  30. end.flatten.compact.join(", ")
  31. end
  32. 1 def email_address? string
  33. 21 string =~ /.+\@.+\..+/
  34. end
  35. 1 def email_address_from_card name, context
  36. 20 card = Card.fetch name
  37. 20 card.account&.email || email_addresses_from_card_content(card, context)
  38. end
  39. 1 def email_addresses_from_card_content card, context
  40. subformat(card).contextual_content(context).split(/[,\n]/)
  41. end
  42. end
  43. end;end;end;end;
  44. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/abstract/email_field.rb ~~

card/tmpsets/set/mod011-card-mod-email/abstract/test_context.rb

84.21% lines covered

19 relevant lines. 16 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (TestContext)
  4. #
  5. 1 module TestContext;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/abstract/test_context.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :core do
  10. 20 return super() if voo.hide? :test_context
  11. card.with_context test_context_card do
  12. super()
  13. end
  14. end
  15. 1 def test_context_card
  16. card.left.fetch(:test_context)&.item_card
  17. end
  18. end
  19. 2 module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
  20. 1 view :core do
  21. 20 voo.hide! :test_context
  22. 20 super()
  23. end
  24. end
  25. 2 module EmailTextFormat; module_parent.send :register_set_format, Card::Format::EmailTextFormat, self; extend Card::Set::AbstractFormat
  26. 1 view :core do
  27. 40 voo.hide! :test_context
  28. 40 super()
  29. end
  30. end
  31. end;end;end;end;
  32. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/abstract/test_context.rb ~~

card/tmpsets/set/mod011-card-mod-email/all/email_html.rb

77.78% lines covered

9 relevant lines. 7 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (EmailHtml)
  4. #
  5. 1 module EmailHtml;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/all/email_html.rb"; end
  8. 2 module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :unknown do
  10. ""
  11. end
  12. 1 view :compact_missing do
  13. ""
  14. end
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/all/email_html.rb ~~

card/tmpsets/set/mod011-card-mod-email/all/email_text.rb

72.73% lines covered

11 relevant lines. 8 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (EmailText)
  4. #
  5. 1 module EmailText;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/all/email_text.rb"; end
  8. 2 module EmailTextFormat; module_parent.send :register_set_format, Card::Format::EmailTextFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :unknown do
  10. ""
  11. end
  12. 1 view :compact_missing do
  13. ""
  14. end
  15. 1 view :last_action, perms: :none, cache: :never do
  16. _render_last_action_verb
  17. end
  18. end
  19. end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/all/email_text.rb ~~

card/tmpsets/set/mod011-card-mod-email/right/bcc.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Bcc" cards
  4. #
  5. 1 module Bcc;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/bcc.rb"; end
  8. 1 include_set Abstract::EmailField
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/bcc.rb ~~

card/tmpsets/set/mod011-card-mod-email/right/cc.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Cc" cards
  4. #
  5. 1 module Cc;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/cc.rb"; end
  8. 1 include_set Abstract::EmailField
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/cc.rb ~~

card/tmpsets/set/mod011-card-mod-email/right/from.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+From" cards
  4. #
  5. 1 module From;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/from.rb"; end
  8. 1 include_set Abstract::EmailField
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/from.rb ~~

card/tmpsets/set/mod011-card-mod-email/right/html_message.rb

91.67% lines covered

12 relevant lines. 11 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+HtmlMessage" cards
  4. #
  5. 1 module HtmlMessage;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/html_message.rb"; end
  8. 1 include_set Abstract::TestContext
  9. 1 def clean_html?
  10. false
  11. end
  12. 2 module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
  13. 1 def email_content context
  14. 20 content = contextual_content context
  15. 20 return unless content.present?
  16. 20 Card::Mailer.layout content
  17. end
  18. end
  19. end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/html_message.rb ~~

card/tmpsets/set/mod011-card-mod-email/right/subject.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Subject" cards
  4. #
  5. 1 module Subject;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/subject.rb"; end
  8. 1 include_set Abstract::TestContext
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/subject.rb ~~

card/tmpsets/set/mod011-card-mod-email/right/text_message.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+TextMessage" cards
  4. #
  5. 1 module TextMessage;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/text_message.rb"; end
  8. 1 include_set Abstract::TestContext
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/text_message.rb ~~

card/tmpsets/set/mod011-card-mod-email/right/to.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+To" cards
  4. #
  5. 1 module To;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/right/to.rb"; end
  8. 1 include_set Abstract::EmailField
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/right/to.rb ~~

card/tmpsets/set/mod011-card-mod-email/type/email_template.rb

71.93% lines covered

57 relevant lines. 41 lines covered and 16 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "EmailTemplate" cards
  4. #
  5. 1 module EmailTemplate;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/type/email_template.rb"; end
  8. 1 def clean_html?
  9. false
  10. end
  11. 1 def deliver context=nil, fields={}, opts={}
  12. 21 mail = format.mail context, fields, opts
  13. 21 mail.deliver
  14. rescue Net::SMTPError => exception
  15. errors.add :exception, exception.message
  16. end
  17. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  18. 1 def mail context=nil, fields={}, opts={}
  19. 21 config = card.email_config context, fields, opts
  20. 21 fmt = self # self is <Mail::Message> within the new_mail block
  21. 21 Card::Mailer.new_mail config do
  22. 21 fmt.message_body self, config
  23. 21 fmt.add_attachments self, config.delete(:attach)
  24. end
  25. end
  26. 1 def message_body mail, config
  27. 21 config[:html_message] &&= config[:html_message].call mail
  28. 21 method, args = body_method_and_args config[:html_message].present?,
  29. config[:text_message].present?
  30. 82 args = Array.wrap(args).map { |arg| config[arg] }
  31. 21 send method, mail, *args
  32. end
  33. 1 def body_method_and_args html, text
  34. 21 if html && text
  35. 20 [:text_and_html_message, %i[text_message html_message attach]]
  36. 1 elsif html
  37. %i[html_body html_message]
  38. else
  39. 1 %i[text_body text_message]
  40. end
  41. end
  42. 1 def text_and_html_message mail, text_message, html_message, attachment_list=nil
  43. 20 fmt = self
  44. 20 if attachment_list&.any?
  45. mail.multipart_mixed text_message, html_message
  46. else
  47. 40 mail.text_part { body text_message }
  48. 40 mail.html_part { fmt.html_body self, html_message }
  49. end
  50. end
  51. 1 def multipart_mixed mail, text_message, html_message
  52. mail.content_type "multipart/mixed"
  53. mail.part content_type: "multipart/alternative" do |copy|
  54. copy.part content_type: "text/plain" do |plain|
  55. plain.body = text_message
  56. end
  57. copy.part content_type: "text/html" do |html|
  58. html.body = html_message
  59. end
  60. end
  61. end
  62. 1 def html_body mail, message
  63. 20 mail.content_type "text/html; charset=UTF-8"
  64. 20 mail.body message
  65. end
  66. 1 def text_body mail, message
  67. 1 mail.content_type "text/plain; charset=UTF-8"
  68. 2 mail.text_part { body message }
  69. end
  70. 1 def add_attachments mail, list
  71. 21 return unless list.present?
  72. each_valid_attachment list do |file, index|
  73. mail.add_file filename: attachment_name(file, index),
  74. content: File.read(file.path)
  75. end
  76. end
  77. 1 def each_valid_attachment list
  78. list.each_with_index do |cardname, index|
  79. next unless (file = Card[cardname]&.try(:attachment))
  80. yield file, index
  81. end
  82. end
  83. 1 def attachment_name file, number
  84. "attachment-#{number + 1}.#{file.extension}"
  85. end
  86. end
  87. end;end;end;end;
  88. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/type/email_template.rb ~~

card/tmpsets/set/mod011-card-mod-email/type/email_template/email_config.rb

95.74% lines covered

47 relevant lines. 45 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Type; module EmailTemplate;
  3. # Set: All "EmailTemplate+EmailConfig" cards (EmailConfig)
  4. #
  5. 1 module EmailConfig;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-email/set/type/email_template/email_config.rb"; end
  8. EMAIL_FIELDS =
  9. 1 %i[to from cc bcc attach subject text_message html_message].freeze
  10. EMAIL_FIELD_METHODS =
  11. 1 { subject: :contextual_content,
  12. text_message: :contextual_content,
  13. attach: :extended_item_contents }.freeze
  14. # @param [Card] context the card in whose context all email fields will be interpreted
  15. # @param [Hash] fields override any templated field configurations with hash values
  16. # @param [Hash] opts options for rendering. unknown options become format options
  17. # @option opts [Card, String, Integer] :auth user identifier. render as this user
  18. 1 def email_config context, fields={}, opts={}
  19. 21 @active_email_context = context || self
  20. 21 auth = opts.delete :auth
  21. 21 config = EMAIL_FIELDS.each_with_object({}) do |field, conf|
  22. 168 conf[field] = fields[field] || email_field_from_card(field, auth, opts)
  23. end
  24. 21 safe_from_and_reply_to! config
  25. 210 config.select { |_k, v| v.present? }
  26. end
  27. 1 def email_field_from_card field, auth, format_opts
  28. 148 return unless (field_card = fetch(field))
  29. 81 auth ||= field_card.updater
  30. 81 special_email_field_method(field, field_card, auth, format_opts) ||
  31. standard_email_field(field, field_card, auth, format_opts)
  32. end
  33. 1 def special_email_field_method field, field_card, auth, format_opts
  34. 81 method = "email_#{field}_field"
  35. 81 return unless respond_to? method
  36. 20 send method, field_card, auth, format_opts
  37. end
  38. 1 def standard_email_field field, field_card, auth, format_opts
  39. 61 method = EMAIL_FIELD_METHODS[field] || :email_addresses
  40. 61 format_opts = format_opts.merge format: :email_text
  41. 61 Auth.as auth do
  42. 61 field_card.format(format_opts).send method, @active_email_context
  43. end
  44. end
  45. # html messages return procs because image attachments can't be properly rendered
  46. # without a mail object. (which isn't available at initial config time)
  47. 1 def email_html_message_field message_card, auth, format_opts
  48. 20 proc do |mail|
  49. 20 Auth.as auth do
  50. 20 format_opts = format_opts.merge format: :email_html, active_mail: mail
  51. 20 message_card.format(format_opts).email_content @active_email_context
  52. end
  53. end
  54. end
  55. # whenever a default "from" field is configured in Card::Mailer, emails are always
  56. # actually "from" that address
  57. 1 def safe_from_and_reply_to! config
  58. 21 conf_name, conf_email = configured_from_name_and_email config[:from]
  59. 21 actual_email = Card::Mailer.default[:from] || conf_email
  60. 21 config[:from] = email_from_field_value conf_name, conf_email, actual_email
  61. 21 config[:reply_to] ||= actual_email
  62. end
  63. 1 def email_from_field_value conf_name, conf_email, actual_email
  64. 21 conf_text = conf_name || conf_email
  65. 21 if conf_text != actual_email
  66. %("#{conf_text}" <#{actual_email}>)
  67. 21 elsif actual_email.present?
  68. 19 actual_email
  69. else
  70. 2 Card[WagnBotID].account.email
  71. end
  72. end
  73. 1 def configured_from_name_and_email raw_string
  74. 21 if raw_string =~ /(.*)\<(.*)>/
  75. [Regexp.last_match(1).strip, Regexp.last_match(2)]
  76. else
  77. 21 [nil, raw_string]
  78. end
  79. end
  80. end;end;end;end;end;
  81. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-email/set/type/email_template/email_config.rb ~~

card/tmpsets/set/mod012-card-mod-account/abstract/account_field.rb

100.0% lines covered

11 relevant lines. 11 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (AccountField)
  4. #
  5. 1 module AccountField;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/abstract/account_field.rb"; end
  8. # allow account owner to update account field content
  9. 1 def ok_to_update
  10. 6 return true if own_account? && !name_changed? && !type_id_changed?
  11. 2 super
  12. end
  13. # force inherit permission on create
  14. # (cannot be done with rule, because sets are not addressable)
  15. 1 def permission_rule_id action
  16. 12 if action == :create
  17. 4 left_permission_rule_id action
  18. else
  19. 8 super
  20. end
  21. end
  22. end;end;end;end;
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/abstract/account_field.rb ~~

card/tmpsets/set/mod012-card-mod-account/abstract/accountable.rb

72.0% lines covered

25 relevant lines. 18 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Accountable)
  4. #
  5. 1 module Accountable;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/abstract/accountable.rb"; end
  8. 1 def account
  9. 117 fetch :account, new: {}
  10. end
  11. 1 def default_account_status
  12. 1 "active"
  13. end
  14. 1 def current_account?
  15. id && Auth.current_id == id
  16. end
  17. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  18. 1 def default_bridge_tab
  19. card.current_account? ? :account_tab : super
  20. end
  21. 1 view :account_tab do
  22. bridge_pill_sections "Account" do
  23. [["Settings", account_details_items],
  24. ["Content", account_content_items]]
  25. end
  26. end
  27. 1 def show_account_tab?
  28. card.account.real?
  29. end
  30. 1 def account_formgroups
  31. 2 Auth.as_bot do
  32. 2 subformat(card.account)._render :content_formgroups, structure: true
  33. end
  34. end
  35. 1 def account_details_items
  36. [
  37. ["Email and Password", :account,
  38. { path: { slot: { hide: %i[help_link bridge_link] } } }],
  39. ["Roles", :roles,
  40. { path: { view: :content_with_edit_button } }],
  41. ["Notifications", :follow]
  42. ]
  43. end
  44. 1 def account_content_items
  45. [["Created", :created],
  46. ["Edited", :edited]]
  47. end
  48. end
  49. end;end;end;end;
  50. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/abstract/accountable.rb ~~

card/tmpsets/set/mod012-card-mod-account/all/account.rb

90.74% lines covered

54 relevant lines. 49 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Account)
  4. #
  5. 1 module Account;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/all/account.rb"; end
  8. 1 module ClassMethods
  9. 1 def default_accounted_type_id
  10. 5 UserID
  11. end
  12. end
  13. 1 def account
  14. 34 fetch :account
  15. end
  16. 1 def parties
  17. 318 @parties ||= (all_enabled_roles << id).flatten.reject(&:blank?)
  18. end
  19. 1 def among? ok_ids
  20. 298 ok_ids.any? do |ok_id|
  21. 298 ok_id == AnyoneID ||
  22. 148 (ok_id == AnyoneWithRoleID && all_enabled_roles.size > 1) ||
  23. parties.member?(ok_id)
  24. end
  25. end
  26. 1 def own_account?
  27. # card is +*account card of signed_in user.
  28. 18 name.part_names[0].key == Auth.as_card.key &&
  29. name.part_names[1].key == Card[:account].key
  30. end
  31. 1 def read_rules
  32. 196 @read_rules ||= fetch_read_rules
  33. end
  34. 1 def read_rules_hash
  35. 3363 @read_rules_hash ||= read_rules.each_with_object({}) { |id, h| h[id] = true }
  36. end
  37. 1 def fetch_read_rules
  38. 170 return [] if id == WagnBotID # always_ok, so not needed
  39. 170 ([AnyoneID] + parties).each_with_object([]) do |party_id, rule_ids|
  40. 573 next unless (cache = Card::Rule.read_rule_cache[party_id])
  41. 170 rule_ids.concat cache
  42. end
  43. end
  44. 1 def clear_roles
  45. 1 @parties = @all_roles = @all_active_roles = @read_rules = nil
  46. end
  47. 1 def with_clear_roles
  48. a, b, c, d = @parties, @all_roles, @all_active_roles, @read_rules
  49. yield
  50. ensure
  51. @parties, @all_roles, @all_active_roles, @read_rules = a, b, c, d
  52. end
  53. 1 def all_enabled_roles
  54. 261 @all_active_roles ||= (id == AnonymousID ? [] : enabled_role_ids)
  55. end
  56. 1 def all_roles
  57. @all_roles ||= (id == AnonymousID ? [] : fetch_roles)
  58. end
  59. 1 def enabled_role_ids
  60. 164 Auth.as_bot do
  61. # workaround for broken migrations
  62. 164 return fetch_roles unless Card::Codename.exists? :enabled_roles
  63. 164 enabled = enabled_roles_card
  64. 164 enabled.virtual? ? enabled.item_ids : fetch_roles
  65. end
  66. end
  67. 1 def enabled_roles_card
  68. 164 fetch :enabled_roles, eager_cache: true, new: { type_id: SessionID }
  69. end
  70. 1 def fetch_roles
  71. 164 [AnyoneSignedInID] + role_ids_from_roles_trait
  72. end
  73. 1 def role_ids_from_roles_trait
  74. 164 Auth.as_bot do
  75. 164 role_trait = fetch :roles
  76. 164 role_trait ? role_trait.item_ids : []
  77. end
  78. end
  79. 1 event :generate_token do
  80. Digest::SHA1.hexdigest "--#{Time.zone.now.to_f}--#{rand 10}--"
  81. end
  82. 1 event :set_stamper, :prepare_to_validate do
  83. 451 self.updater_id = Auth.current_id
  84. 451 self.creator_id = updater_id if new_card?
  85. end
  86. end;end;end;end;
  87. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/all/account.rb ~~

card/tmpsets/set/mod012-card-mod-account/right/account.rb

91.43% lines covered

35 relevant lines. 32 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Account" cards
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module Account;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/account.rb"; end
  9. 1 card_accessor :email
  10. 1 card_accessor :password
  11. 1 card_accessor :salt
  12. 1 card_accessor :status
  13. 1 card_accessor :api_key
  14. 1 require_field :email
  15. 1 def accounted
  16. 3 left
  17. end
  18. 1 def accounted_id
  19. 8 left_id
  20. end
  21. 1 def ok_to_read
  22. 5 own_account? ? true : super
  23. end
  24. # allow account owner to update account field content
  25. 1 def ok_to_update
  26. 3 return true if own_account? && !name_changed? && !type_id_changed?
  27. 1 super
  28. end
  29. 1 def changes_visible? act
  30. 17 act.actions_affecting(act.card).each do |action|
  31. 17 return true if action.card.ok? :read
  32. end
  33. false
  34. end
  35. 1 def send_account_email email_template
  36. 3 ecard = Card[email_template]
  37. 3 unless ecard&.type_id == EmailTemplateID
  38. raise Card::Error, "invalid email template: #{email_template}"
  39. end
  40. 3 ecard.deliver self, to: email
  41. end
  42. 1 def validate_api_key! api_key
  43. api_key_card.validate! api_key
  44. end
  45. 1 def method_missing method, *args
  46. 72 super unless args.empty? && (matches = method.match(/^(?<status>.*)\?$/))
  47. 72 status == matches[:status]
  48. end
  49. 1 def respond_to_missing? method, _include_private=false
  50. 8 method.match?(/\?/) ? true : super
  51. end
  52. end;end;end;end;
  53. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/account.rb ~~

card/tmpsets/set/mod012-card-mod-account/right/account/events.rb

86.54% lines covered

52 relevant lines. 45 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Right; module Account;
  3. # Set: All "+Account+Events" cards (Events)
  4. #
  5. #### ON CREATE
  6. 1 module Events;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/account/events.rb"; end
  9. 1 event :set_default_salt, :prepare_to_validate, on: :create do
  10. 2 add_subfield(:salt).generate
  11. end
  12. 1 event :set_default_status, :prepare_to_validate, on: :create do
  13. 2 add_subfield :status, content: (accounted&.try(:default_account_status) || "active")
  14. end
  15. # ON UPDATE
  16. # reset password emails contain a link to update the +*account card
  17. # and trigger this event
  18. 1 event :reset_password, :prepare_to_validate, on: :update, trigger: :required do
  19. 1 verifying_token :reset_password_success, :reset_password_failure
  20. end
  21. 1 event :verify_and_activate, :prepare_to_validate, on: :update, trigger: :required do
  22. 1 activatable do
  23. 1 verifying_token :verify_and_activate_success, :verify_and_activate_failure
  24. 1 add_subcard(accounted)&.try :activate_accounted
  25. end
  26. end
  27. 1 event :password_redirect, :finalize, on: :update, when: :password_redirect? do
  28. success << { id: name, view: "edit" }
  29. end
  30. # INTEGRATION
  31. 1 %i[password_reset_email verification_email welcome_email].each do |email|
  32. 3 event "send_#{email}".to_sym, :integrate, trigger: :required do
  33. 3 send_account_email email
  34. end
  35. end
  36. ## EVENT HELPERS
  37. 1 def activatable
  38. 1 abort :failure, "no field manipulation mid-activation" if subcards.present?
  39. # above is necessary because activation uses super user (Decko Bot),
  40. # so allowing subcards would be unsafe
  41. 1 yield
  42. end
  43. # note: this only works in the context of an action.
  44. # if run independently, it will not activate an account
  45. 1 event :activate_account do
  46. 1 add_subfield :status, content: "active"
  47. 1 trigger_event! :send_welcome_email
  48. end
  49. 1 def verifying_token success, failure
  50. 2 requiring_token do |token|
  51. 2 result = Auth::Token.decode token
  52. 2 if result.is_a?(String)
  53. aborting { send failure, result }
  54. else
  55. 2 send success
  56. end
  57. end
  58. end
  59. 1 def requiring_token
  60. 2 if !(token = Env.params[:token])
  61. aborting { errors.add :token, "is required" }
  62. else
  63. 2 yield token
  64. end
  65. end
  66. 1 def password_redirect?
  67. 2 Auth.current_id == accounted_id && password.blank?
  68. end
  69. 1 def verify_and_activate_success
  70. 1 Auth.signin accounted_id
  71. 1 Auth.as_bot # use admin permissions for rest of action
  72. 1 activate_account
  73. 1 success << ""
  74. end
  75. 1 def verify_and_activate_failure error_message
  76. send_verification_email
  77. errors.add "Sorry, #{error_message}. Please check your email for a new activation link."
  78. end
  79. 1 def reset_password_success
  80. 1 Auth.signin accounted_id
  81. 1 success << { id: name, view: :edit }
  82. 1 abort :success
  83. end
  84. 1 def reset_password_failure error_message
  85. Auth.as_bot { send_password_reset_email }
  86. errors.add tr(:sorry_email_reset, error_msg: error_message)
  87. end
  88. end;end;end;end;end;
  89. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/account/events.rb ~~

card/tmpsets/set/mod012-card-mod-account/right/account/views.rb

88.57% lines covered

35 relevant lines. 31 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Right; module Account;
  3. # Set: All "+Account+Views" cards (Views)
  4. #
  5. 1 module Views;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/account/views.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 view :verify_url, cache: :never do
  10. 2 raise Error::PermissionDenied unless card.ok?(:create) || card.action
  11. 2 token_url :verify_and_activate, anonymous: true
  12. end
  13. 1 view :reset_password_url do
  14. 2 raise Error::PermissionDenied unless card.password_card.ok? :update
  15. 2 token_url :reset_password
  16. end
  17. 1 view :token_expiry do
  18. "(#{token_expiry_sentence}"
  19. end
  20. 1 view :token_days do
  21. 4 Card.config.token_expiry / 1.day
  22. end
  23. # DEPRECATED
  24. 1 view :verify_days, :token_days
  25. 1 view :reset_password_days, :token_days
  26. 1 def token_url trigger, extra_payload={}
  27. 4 card_url path(action: :update,
  28. card: { trigger: trigger },
  29. token: new_token(extra_payload))
  30. end
  31. 1 def token_expiry_sentence
  32. "Link will expire in #{render_token_days} days"
  33. end
  34. 1 def new_token extra_payload
  35. 4 Auth::Token.encode card.accounted_id, extra_payload
  36. end
  37. end
  38. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  39. 1 view :core do
  40. 3 [account_field_nest(:email, "email"),
  41. account_field_nest(:password, "password")]
  42. end
  43. 1 def account_field_nest field, title
  44. 6 field_nest field, title: title, view: :labeled
  45. # edit: :inline, hide: [:help_link, :bridge_link]
  46. end
  47. 1 before :content_formgroups do
  48. 3 voo.edit_structure = [[:email, "email"], [:password, "password"]]
  49. end
  50. 1 view :token_expiry do
  51. "<p><em>#{token_expiry_sentence}</em></p>"
  52. end
  53. end
  54. 2 module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
  55. 1 def mail context, fields
  56. super context, fields.reverse_merge(to: card.email)
  57. end
  58. end
  59. end;end;end;end;end;
  60. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/account/views.rb ~~

card/tmpsets/set/mod012-card-mod-account/right/api_key.rb

57.14% lines covered

14 relevant lines. 8 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+ApiKey" cards
  4. #
  5. 1 module ApiKey;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/api_key.rb"; end
  8. 1 include_set Abstract::AccountField
  9. # DURATIONS = "second|minute|hour|day|week|month|year".freeze
  10. 1 def history?
  11. false
  12. end
  13. 1 view :raw do
  14. tr :private_data
  15. end
  16. 1 def validate! api_key
  17. error =
  18. case
  19. when !real? then [:token_not_found, tr(:error_token_not_found)]
  20. # when expired? then [:token_expired, tr(:error_token_expired)]
  21. when content != api_key then [:incorrect_token, tr(:error_incorrect_token)]
  22. end
  23. errors.add(*error) if error
  24. error.nil?
  25. end
  26. # def expired?
  27. # !permanent? && updated_at <= term.ago
  28. # end
  29. #
  30. # def permanent?
  31. # term == "permanent"
  32. # end
  33. # def term
  34. # @term ||=
  35. # if expiration.present?
  36. # term_from_string expiration
  37. # else
  38. # Card.config.token_expiry
  39. # end
  40. # end
  41. # def term_from_string string
  42. # string.strip!
  43. # return "permanent" if string == "none"
  44. # re_match = /^(\d+)[\.\s]*(#{DURATIONS})s?$/.match(string)
  45. # number, unit = re_match.captures if re_match
  46. # raise Card::Open::Error, tr(:exception_bad_expiration, example: '2 days') unless unit
  47. # number.to_i.send unit
  48. # end
  49. end;end;end;end;
  50. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/api_key.rb ~~

card/tmpsets/set/mod012-card-mod-account/right/email.rb

81.48% lines covered

27 relevant lines. 22 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Email" cards
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module Email;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/email.rb"; end
  9. 1 include_set Abstract::AccountField
  10. 1 event :validate_email, :validate, on: :save do
  11. 3 if content? && content !~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
  12. errors.add :content, tr(:error_invalid_address)
  13. end
  14. end
  15. 1 event :validate_unique_email, after: :validate_email, on: :save do
  16. 3 if content.present?
  17. 3 Auth.as_bot do
  18. 3 cql = { right_id: EmailID, eq: content, return: :id }
  19. 3 cql[:not] = { id: id } if id
  20. 3 cql_comment = tr(:search_email_duplicate, content: content)
  21. 3 if Card.search(cql, cql_comment).first
  22. errors.add :content, tr(:error_not_unique)
  23. end
  24. end
  25. end
  26. end
  27. 1 event :downcase_email, :prepare_to_validate, on: :save do
  28. 75 return if !content || content == content.downcase
  29. self.content = content.downcase
  30. end
  31. 1 def email_required?
  32. !left.system?
  33. end
  34. 1 def ok_to_read
  35. 4 if own_email? || Auth.always_ok?
  36. 4 true
  37. else
  38. deny_because tr(:deny_email_restricted)
  39. end
  40. end
  41. 1 def own_email?
  42. 4 name.part_names[0].key == Auth.as_card.key
  43. end
  44. end;end;end;end;
  45. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/email.rb ~~

card/tmpsets/set/mod012-card-mod-account/right/password.rb

96.55% lines covered

29 relevant lines. 28 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Password" cards
  4. #
  5. 1 module Password;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/password.rb"; end
  8. 1 include_set Abstract::AccountField
  9. 1 def history?
  10. 78 false
  11. end
  12. 1 def ok_to_read
  13. 3 own_account? ? true : super
  14. end
  15. 1 event :encrypt_password, :store,
  16. on: :save, changed: :content,
  17. 3 when: proc { !Card::Env[:no_password_encryptions] } do
  18. # no_password_encryptions = hack for import - fix with api for ignoring events
  19. 3 salt = left&.salt
  20. 3 self.content = Auth.encrypt content, salt
  21. # errors.add :password, 'need a valid salt'
  22. # turns out we have a lot of existing account without a salt.
  23. # not sure when that broke??
  24. end
  25. 1 event :validate_password, :validate, on: :save do
  26. 3 return if content.length > 3
  27. errors.add :password, tr(:password_length)
  28. end
  29. 1 event :validate_password_present, :prepare_to_validate, on: :update do
  30. 1 abort :success if content.blank?
  31. end
  32. 1 view :raw do
  33. 3 tr :encrypted
  34. end
  35. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  36. 1 view :core, wrap: :em do
  37. 3 render_raw
  38. end
  39. 1 view :input do
  40. 81 card.content = ""
  41. 81 password_field :content, class: "d0-card-content", autocomplete: autocomplete?
  42. end
  43. 1 def autocomplete?
  44. 81 return "on" if @parent && @parent.card.name == "*signin+*account" # HACK
  45. 81 "off"
  46. end
  47. end
  48. end;end;end;end;
  49. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/password.rb ~~

card/tmpsets/set/mod012-card-mod-account/right/roles.rb

94.74% lines covered

19 relevant lines. 18 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Roles" cards
  4. #
  5. 1 module Roles;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/roles.rb"; end
  8. 1 event :validate_permission_to_assign_roles, :validate, on: :save do
  9. 1 return unless (fr = forbidden_roles).present?
  10. errors.add :permission_denied,
  11. "You don't have permission to assign the role#{'s' if fr.size > 1} "\
  12. "#{fr.map(&:name).to_sentence}" # LOCALIZE
  13. end
  14. 1 def forbidden_roles
  15. # restore old roles for permission check
  16. 1 with_old_role_permissions do |new_roles|
  17. 1 new_roles.select do |card|
  18. 3 !Card.fetch(card, "*members").ok? :update
  19. end
  20. end
  21. end
  22. 1 def with_old_role_permissions
  23. 1 new_roles = item_cards
  24. 1 new_content = content
  25. 1 left.clear_roles
  26. 1 Auth.update_always_cache Card::Auth.as_id, nil
  27. 1 self.content = db_content_before_act
  28. 1 yield new_roles
  29. ensure
  30. 1 self.content = new_content
  31. end
  32. end;end;end;end;
  33. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/roles.rb ~~

card/tmpsets/set/mod012-card-mod-account/right/salt.rb

90.91% lines covered

11 relevant lines. 10 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Salt" cards
  4. #
  5. 1 module Salt;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/salt.rb"; end
  8. 1 include_set Abstract::AccountField
  9. 1 def generate
  10. 2 self.content = Digest::SHA1.hexdigest "--#{Time.zone.now}--"
  11. end
  12. 1 def history?
  13. 4 false
  14. end
  15. 1 view :raw do
  16. tr :private_data
  17. end
  18. end;end;end;end;
  19. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/salt.rb ~~

card/tmpsets/set/mod012-card-mod-account/right/status.rb

78.57% lines covered

14 relevant lines. 11 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Status" cards
  4. #
  5. 1 module Status;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/right/status.rb"; end
  8. 1 include_set Abstract::AccountField
  9. 1 include_set Abstract::Pointer
  10. 1 def input_type
  11. :radio
  12. end
  13. 1 def option_names
  14. %w[unapproved unverified active blocked system]
  15. end
  16. 1 def ok_to_update
  17. 1 if own_account? && !Auth.always_ok?
  18. deny_because you_cant(tr(:deny_not_change_own_account))
  19. else
  20. 1 super
  21. end
  22. end
  23. end;end;end;end;
  24. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/right/status.rb ~~

card/tmpsets/set/mod012-card-mod-account/self/signin.rb

80.67% lines covered

119 relevant lines. 96 lines covered and 23 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Signin"
  4. #
  5. # The Sign In card manages logging in and out of the site.
  6. #
  7. 1 module Signin;
  8. 1 extend Card::Set
  9. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/self/signin.rb"; end
  10. # /:signin (core view) gives the login ui
  11. # /:signin?view=edit gives the forgot password ui
  12. # /update/:signin is the login action
  13. # /delete/:signin is the logout action
  14. # authentication event
  15. 1 event :signin, :validate, on: :update do
  16. 71 email = subfield :email
  17. 71 email &&= email.content
  18. 71 pword = subfield :password
  19. 71 pword &&= pword.content
  20. 71 authenticate_or_abort email, pword
  21. end
  22. # abort after successful signin (do not save card)
  23. 1 event :signin_success, after: :signin do
  24. 71 abort :success
  25. end
  26. 1 event :signout, :validate, on: :delete do
  27. 6 Env.reset_session
  28. 6 Auth.signin AnonymousID
  29. 6 abort :success
  30. end
  31. # triggered by clicking "Reset my Password", this sends out the verification password
  32. # and aborts (does not sign in)
  33. 1 event :send_reset_password_token, before: :signin, on: :update, trigger: :required do
  34. 1 email = subfield(:email)&.content
  35. 1 send_reset_password_email_or_fail email
  36. end
  37. 1 def ok_to_read
  38. 79 true
  39. end
  40. 1 def recaptcha_on?
  41. 158 false
  42. end
  43. 1 def i18n_signin key
  44. 1 I18n.t key, scope: "mod.card-mod-account.set.self.signin"
  45. end
  46. 1 def authenticate_or_abort email, pword
  47. 71 abort_unless email, :email_missing
  48. 71 abort_unless pword, :password_missing
  49. 71 authenticate_and_signin(email, pword) || failed_signin(email)
  50. end
  51. 1 def authenticate_and_signin email, pword
  52. 71 return unless (account = Auth.authenticate email, pword)
  53. 71 Auth.signin account.left_id
  54. end
  55. 1 def failed_signin email
  56. errors.add :signin, signin_error_message(account_for(email))
  57. abort :failure
  58. end
  59. 1 def abort_unless value, error_key
  60. 142 abort :failure, i18n_signin(error_key) unless value
  61. end
  62. 1 def signin_error_message account
  63. case
  64. when account.nil? then i18n_signin(:error_unknown_email)
  65. when !account.active? then i18n_signin(:error_not_active)
  66. else i18n_signin(:error_wrong_password)
  67. end
  68. end
  69. 1 def error_on field, error_key
  70. errors.add field, i18n_signin(error_key)
  71. end
  72. 1 def account_for email
  73. 1 Auth.find_account_by_email email
  74. end
  75. 1 def send_reset_password_email_or_fail email
  76. aborting do
  77. break errors.add :email, i18n_signin(:error_blank) if email.blank?
  78. if (account = Auth.find_account_by_email(email))&.active?
  79. Auth.as_bot { account.send_password_reset_email }
  80. elsif account
  81. errors.add :account, i18n_signin(:error_not_active)
  82. else
  83. errors.add :email, i18n_signin(:error_not_recognized)
  84. end
  85. end
  86. end
  87. 1 def send_reset_password_email_or_fail email
  88. 1 aborting do
  89. 1 break if blank_email? email
  90. 1 if (account = account_for email)&.active?
  91. 1 send_reset_password_email account
  92. else
  93. reset_password_fail account
  94. end
  95. end
  96. end
  97. 1 def blank_email? email
  98. 1 return false if email.present?
  99. error_on :email, :error_blank
  100. end
  101. 1 def send_reset_password_email account
  102. 2 Auth.as_bot { account.send_password_reset_email }
  103. end
  104. 1 def reset_password_fail account
  105. if account
  106. error_on :account, :error_not_active
  107. else
  108. error_on :email, :error_not_recognized
  109. end
  110. end
  111. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  112. 1 view :core, cache: :never do
  113. 78 voo.edit_structure = [signin_field(:email), signin_field(:password)]
  114. 78 with_nest_mode :edit do
  115. 78 card_form :update, recaptcha: :off, success: signin_success do
  116. [
  117. 78 _render_content_formgroups,
  118. _render_signin_buttons
  119. ]
  120. end
  121. end
  122. end
  123. 1 view :open do
  124. voo.show :help
  125. voo.hide :menu
  126. super()
  127. end
  128. # FIXME: need a generic solution for this
  129. 1 view :title do
  130. 80 voo.title ||= I18n.t(:sign_in_title, scope: "mod.card-mod-account.set.self.signin")
  131. 80 super()
  132. end
  133. 1 view :open_content do
  134. # annoying step designed to avoid table of contents. sigh
  135. _render_core
  136. end
  137. 1 view :one_line_content do
  138. ""
  139. end
  140. 1 view :reset_password_success do
  141. # 'Check your email for a link to reset your password'
  142. 2 frame { I18n.t(:check_email, scope: "mod.card-mod-account.set.self.signin") }
  143. end
  144. 1 view :signin_buttons do
  145. 78 button_formgroup do
  146. 78 [signin_button, signup_link, reset_password_link]
  147. end
  148. end
  149. # FORGOT PASSWORD
  150. 1 view :edit do
  151. 1 reset_password_voo
  152. 2 Auth.as_bot { super() }
  153. end
  154. 1 def reset_password_voo
  155. 1 voo.title ||= card.i18n_signin(:forgot_password)
  156. 1 voo.edit_structure = [signin_field(:email)]
  157. 1 voo.hide :help
  158. end
  159. 1 view :edit_buttons do
  160. 1 text = I18n.t :reset_my_password, scope: "mod.card-mod-account.set.self.signin"
  161. 1 button_tag text, situation: "primary", class: "_close-modal-on-success"
  162. end
  163. 1 def signin_success
  164. 78 "REDIRECT: #{Env.interrupted_action || '*previous'}"
  165. end
  166. 1 def signin_button
  167. 78 text = I18n.t :sign_in, scope: "mod.card-mod-account.set.self.signin"
  168. 78 button_tag text, situation: "primary"
  169. end
  170. 1 def signup_link
  171. 78 text = I18n.t :or_sign_up, scope: "mod.card-mod-account.set.self.signin"
  172. 78 subformat(Card[:account_links]).render! :sign_up, title: text
  173. end
  174. 1 def reset_password_link
  175. 78 text = I18n.t :reset_password, scope: "mod.card-mod-account.set.self.signin"
  176. 78 link = link_to_view :edit, text, path: { slot: { hide: :bridge_link } }
  177. # FIXME: inline styling
  178. 78 raw("<div style='float:right'>#{link}</div>")
  179. end
  180. 1 def edit_view_hidden
  181. 1 hidden_tags card: { trigger: :send_reset_password_token }
  182. end
  183. 1 def edit_success
  184. 1 { view: :reset_password_success }
  185. end
  186. 1 def signin_field name
  187. 157 nest_name = "".to_name.trait(name)
  188. 157 [nest_name, { title: name.to_s, view: "titled",
  189. nest_name: nest_name, skip_perms: true }]
  190. end
  191. end
  192. end;end;end;end;
  193. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/self/signin.rb ~~

card/tmpsets/set/mod012-card-mod-account/type/role.rb

60.0% lines covered

15 relevant lines. 9 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Role" cards
  4. #
  5. 1 module Role;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type/role.rb"; end
  8. 1 def disabled?
  9. Auth.current&.fetch(:disabled_roles)&.item_ids&.include? id
  10. end
  11. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  12. 1 view :link_with_checkbox, cache: :never do
  13. role_checkbox
  14. end
  15. 1 def role_checkbox
  16. name = card.disabled? ? "add_item" : "drop_item"
  17. subformat(Auth.current.field(:disabled_roles, new: {})).card_form :update do
  18. [check_box_tag(name, card.id, !card.disabled?, class: "_edit-item"),
  19. render_link]
  20. end
  21. end
  22. 1 def related_by_content_items
  23. super.unshift ["members", :members]
  24. end
  25. end
  26. end;end;end;end;
  27. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type/role.rb ~~

card/tmpsets/set/mod012-card-mod-account/type/signup.rb

78.13% lines covered

32 relevant lines. 25 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Signup" cards
  4. #
  5. 1 module Signup;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type/signup.rb"; end
  8. 1 include_set Abstract::Accountable
  9. 1 require_field :account
  10. 1 def default_account_status
  11. 1 can_approve? ? "unverified" : "unapproved"
  12. end
  13. 1 def can_approve?
  14. 2 Card.new(type_id: Card.default_accounted_type_id).ok? :create
  15. end
  16. 1 def activate_accounted
  17. 1 self.type_id = Card.default_accounted_type_id
  18. end
  19. 1 event :auto_approve_with_verification, :validate, on: :create, when: :can_approve? do
  20. 1 request_verification
  21. end
  22. 1 event :approve_with_verification, :validate, on: :update, trigger: :required do
  23. approvable { request_verification }
  24. end
  25. 1 event :approve_without_verification, :validate, on: :update, trigger: :required do
  26. # TODO: if validated here, add trigger and activate in storage phase
  27. approvable do
  28. activate_accounted
  29. account_subfield.activate_account
  30. end
  31. end
  32. 1 event :act_as_current_for_integrate_stage, :integrate, on: :create do
  33. # needs justification!
  34. 1 Auth.current_id = id
  35. end
  36. 1 def account_subfield
  37. 1 subfield(:account) || add_subfield(:account)
  38. end
  39. 1 def request_verification
  40. 1 acct = account_subfield
  41. 1 acct.add_subfield :status, content: "unverified"
  42. 1 acct.trigger_event! :send_verification_email
  43. end
  44. 1 def approvable
  45. if can_approve?
  46. yield
  47. else
  48. abort :failure, "illegal approval" # raise permission denied?
  49. end
  50. end
  51. end;end;end;end;
  52. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type/signup.rb ~~

card/tmpsets/set/mod012-card-mod-account/type/signup/views.rb

54.55% lines covered

55 relevant lines. 30 lines covered and 25 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Type; module Signup;
  3. # Set: All "Signup+Views" cards (Views)
  4. #
  5. 1 module Views;
  6. 1 extend Card::Set
  7. 2 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type/signup/views.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def invitation?
  10. 2 Auth.signed_in? && card.can_approve?
  11. end
  12. 1 view :new do
  13. 1 voo.title = invitation? ? tr(:invite) : tr(:sign_up)
  14. 1 super()
  15. end
  16. 1 view :content_formgroups do
  17. 1 [account_formgroups, (card.structure ? edit_slot : "")].join
  18. end
  19. 1 view :new_buttons do
  20. 1 button_formgroup do
  21. 1 [standard_create_button, invite_button].compact
  22. end
  23. end
  24. 1 def invite_button
  25. 1 return unless invitation?
  26. button_tag "Send Invitation", situation: "primary"
  27. end
  28. 1 view :core, template: :haml do
  29. @lines = [signup_line] + account_lines
  30. @body = process_content _render_raw
  31. end
  32. 1 def signup_line
  33. ["<strong>#{safe_name}</strong>",
  34. ("was" if invited?),
  35. "signed up on #{format_date card.created_at}"].compact.join " "
  36. end
  37. 1 def invited?
  38. !self_signup?
  39. end
  40. 1 def self_signup?
  41. card.creator_id == AnonymousID
  42. end
  43. 1 def account_lines
  44. if card.account
  45. verification_lines
  46. else
  47. [tr(:missing_account)]
  48. end
  49. end
  50. 1 def verification_lines
  51. [verification_sent_line, verification_link_line].compact
  52. end
  53. 1 def verification_sent_line
  54. account = card.account
  55. return unless account.email_card.ok?(:read)
  56. "A verification email has been sent to #{account.email}"
  57. end
  58. 1 def verification_link_line
  59. links = verification_links
  60. return if links.empty?
  61. links.join " "
  62. end
  63. 1 def verification_links
  64. [approve_with_token_link, approve_without_token_link, deny_link].compact
  65. end
  66. 1 def approve_with_token_link
  67. action = card.account.status == "unverified" ? "Resend" : "Send"
  68. approval_link "#{action} verification email", :with
  69. end
  70. 1 def approve_without_token_link
  71. approval_link "Approve without verification", :without
  72. end
  73. 1 def approval_link text, with_or_without
  74. return unless card.can_approve?
  75. link_to_card card, text,
  76. path: { action: :update,
  77. card: { trigger: "approve_#{with_or_without}_verification" } }
  78. end
  79. 1 def deny_link
  80. return unless card.ok? :delete
  81. link_to_card card, "Deny and delete", path: { action: :delete }
  82. end
  83. end
  84. end;end;end;end;end;
  85. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type/signup/views.rb ~~

card/tmpsets/set/mod012-card-mod-account/type/user.rb

100.0% lines covered

36 relevant lines. 36 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "User" cards
  4. #
  5. 1 module User;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type/user.rb"; end
  8. 1 include Basic
  9. 1 include_set Abstract::Accountable
  10. 1 attr_accessor :email
  11. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  12. 2 view :setup, unknown: true, perms: ->(_fmt) { Auth.needs_setup? } do
  13. 1 with_nest_mode :edit do
  14. 1 voo.title = "Your deck is ready to go!" # LOCALIZE
  15. 1 voo.show! :help
  16. 1 voo.hide! :menu
  17. 1 voo.help = haml :setup_help
  18. 2 Auth.as_bot { setup_form }
  19. end
  20. end
  21. 1 def setup_form
  22. 1 frame_and_form :create do
  23. [
  24. 1 setup_hidden_fields,
  25. _render_name_formgroup,
  26. account_formgroups,
  27. setup_form_buttons
  28. ]
  29. end
  30. end
  31. 1 def setup_form_buttons
  32. 2 button_formgroup { setup_button }
  33. end
  34. 1 def setup_button
  35. 1 submit_button text: "Set up", disable_with: "Setting up"
  36. end
  37. 1 def setup_hidden_fields
  38. 1 hidden_tags(
  39. setup: true,
  40. success: "REDIRECT: #{path mark: ''}",
  41. "card[type_id]" => Card.default_accounted_type_id
  42. )
  43. end
  44. end
  45. 1 def setup?
  46. 3 Card::Env.params[:setup]
  47. end
  48. 1 event :setup_as_bot, before: :check_permissions, on: :create, when: :setup? do
  49. 1 abort :failure unless Auth.needs_setup?
  50. 1 Auth.as_bot
  51. # we need bot authority to set the initial administrator roles
  52. # this is granted and inspected here as a separate event for
  53. # flexibility and security when configuring initial setups
  54. end
  55. 1 event :setup_first_user, :prepare_to_store, on: :create, when: :setup? do
  56. 1 add_subcard "signup alert email+*to", content: name
  57. 1 add_subfield :roles, content: roles_for_first_user
  58. end
  59. 1 def roles_for_first_user
  60. 1 %i[help_desk shark administrator].map(&:cardname)
  61. end
  62. 1 event :signin_after_setup, :integrate, on: :create, when: :setup? do
  63. 1 Auth.signin id
  64. end
  65. end;end;end;end;
  66. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type/user.rb ~~

card/tmpsets/set/mod012-card-mod-account/type_plus_right/user/email.rb

70.0% lines covered

10 relevant lines. 7 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class TypePlusRight; module User;
  3. # Set: All "+Email" cards on "User" cards
  4. #
  5. # supports legacy references to <User>+*email
  6. 1 module Email;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-account/set/type_plus_right/user/email.rb"; end
  9. # (standard representation is now <User>+*account+*email)
  10. 1 view :raw do
  11. card.content_email || card.account_email || ""
  12. end
  13. 1 def content_email
  14. content if real?
  15. end
  16. 1 def account_email
  17. left&.account&.email
  18. end
  19. end;end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-account/set/type_plus_right/user/email.rb ~~

card/tmpsets/set/mod013-navbar/abstract/account_dropdown.rb

100.0% lines covered

16 relevant lines. 16 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (AccountDropdown)
  4. #
  5. 1 module AccountDropdown;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/abstract/account_dropdown.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def link_to_mycard
  10. 202 link_to_card Auth.current.name, nil,
  11. id: "my-card-link", class: "nav-link #{classy('my-card')}"
  12. end
  13. 1 def account_dropdown &render_role_item
  14. 202 split_button link_to_mycard, nil do
  15. [
  16. 202 link_to_card([Auth.current, :account_settings], "Account"),
  17. 202 (["Roles", role_items(&render_role_item)] if special_roles?)
  18. ]
  19. end
  20. end
  21. 1 def special_roles?
  22. 202 Auth.current_roles.size > 1
  23. end
  24. 1 def role_items
  25. 190 Auth.current_roles.map do |role_name|
  26. 626 yield role_name
  27. end
  28. end
  29. end
  30. end;end;end;end;
  31. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/abstract/account_dropdown.rb ~~

card/tmpsets/set/mod013-navbar/abstract/pointer/html_views.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Pointer;
  3. # Set: Abstract (Pointer, HtmlViews)
  4. #
  5. 1 module HtmlViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/abstract/pointer/html_views.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :nav_item do
  10. 224 nav_dropdown
  11. end
  12. end
  13. end;end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/abstract/pointer/html_views.rb ~~

card/tmpsets/set/mod013-navbar/all/navbar_links.rb

93.33% lines covered

30 relevant lines. 28 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (NavbarLinks)
  4. #
  5. 1 module NavbarLinks;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/all/navbar_links.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :navbar_links, perms: :none do
  10. 448 wrap_with :ul, class: "navbar-nav" do
  11. 448 navbar_items
  12. end
  13. end
  14. # Iterates over all nests and links and renders them as bootstrap navbar items.
  15. # Items that are pointer cards become dropdowns
  16. 1 def navbar_items view: :nav_item, link_class: "nav-link"
  17. 448 process_content nil, chunk_list: :references do |chunk|
  18. 1792 case chunk
  19. when Card::Content::Chunk::Link
  20. 1792 link = chunk.render_link view: view, explicit_link_opts: { class: link_class }
  21. 1792 chunk.explicit_link? && view == :nav_item ? wrap_with_nav_item(link) : link
  22. when Card::Content::Chunk::Nest
  23. content_nest chunk.options.merge view: view
  24. else
  25. chunk.process_chunk
  26. end
  27. end
  28. end
  29. # overridden in Abstact::Pointer to render dropdown
  30. 1 view :nav_item do
  31. 224 wrap_with_nav_item link_view(class: "nav-link")
  32. end
  33. 1 def wrap_with_nav_item content
  34. 1120 wrap_with(:li, content, class: "nav-item")
  35. end
  36. 1 view :nav_link_in_dropdown do
  37. 448 link_to_card card, render_title, class: "dropdown-item"
  38. end
  39. 1 def nav_dropdown
  40. 224 wrap_with(:li, class: "nav-item dropdown") do
  41. [
  42. 224 dropdown_toggle_link,
  43. dropdown_menu
  44. ]
  45. end
  46. end
  47. 1 def dropdown_toggle_link
  48. 224 link_to(render_title, href: "#", class: "nav-link dropdown-toggle",
  49. "data-toggle": "dropdown")
  50. end
  51. 1 def dropdown_menu
  52. 224 wrap_with :div, dropdown_menu_items, class: "dropdown-menu"
  53. end
  54. 1 def dropdown_menu_items
  55. 224 navbar_items view: :nav_link_in_dropdown, link_class: "dropdown-item"
  56. end
  57. end
  58. end;end;end;end;
  59. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/all/navbar_links.rb ~~

card/tmpsets/set/mod013-navbar/right/enabled_roles.rb

77.14% lines covered

35 relevant lines. 27 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+EnabledRoles" cards
  4. #
  5. 1 module EnabledRoles;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/right/enabled_roles.rb"; end
  8. 1 include_set Abstract::AccountDropdown
  9. 1 def ok_to_read
  10. true
  11. end
  12. 1 def ok_to_update
  13. left_id == Auth.current_id
  14. end
  15. 1 def ok_to_create
  16. left_id == Auth.current_id
  17. end
  18. 1 def ensure_roles
  19. 190 self.content = Auth.current_roles.to_pointer_content if content.blank?
  20. end
  21. 1 event :validate_role_enabling, :validate, on: :save do
  22. illegal_roles = item_names - Auth.current_roles
  23. return if illegal_roles.empty?
  24. errors.add :content, "illegal roles: #{illegal_roles.to_sentence}" # LOCALIZE
  25. end
  26. 1 event :clear_roles_cache, :prepare_to_store, before: :store_in_session do
  27. clear_roles
  28. Auth.update_always_cache Auth.as_id, nil
  29. end
  30. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  31. # permission change compared to super
  32. 1 view :edit_inline, perms: :none, unknown: true, cache: :never, wrap: :slot do
  33. 190 super()
  34. end
  35. 1 def input_type
  36. 190 :checkbox
  37. end
  38. 1 def edit_success
  39. 190 { reload: true }
  40. end
  41. 1 def hidden_form_tags _action, opts
  42. 190 "#{super} #{hidden_tags card: { type_id: SessionID }}"
  43. end
  44. 1 def checkbox_input
  45. 190 card.ensure_roles
  46. 190 wrap_with :div, class: "pointer-checkbox-list" do
  47. 190 account_dropdown &method(:role_item_checkbox)
  48. end
  49. end
  50. 1 def role_item_checkbox role_name
  51. 626 haml :role_checkbox, id: "pointer-checkbox-#{role_name.to_name.key}",
  52. checked: card.item_names.include?(role_name),
  53. option_name: role_name
  54. end
  55. end
  56. end;end;end;end;
  57. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/right/enabled_roles.rb ~~

card/tmpsets/set/mod013-navbar/self/account_links.rb

85.71% lines covered

49 relevant lines. 42 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "AccountLinks"
  4. #
  5. 1 module AccountLinks;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/self/account_links.rb"; end
  8. 1 include_set Abstract::AccountDropdown
  9. 1 def ok_to_read
  10. true
  11. end
  12. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  13. 1 view :core, cache: :never do
  14. status_class = Auth.signed_in? ? "logged-in" : "logged-out"
  15. wrap_with :div, id: "logging", class: status_class do
  16. navbar_items.join "\n"
  17. end
  18. end
  19. 1 def navbar_items
  20. # removed invite for now
  21. links =
  22. 224 %i[my_card sign_out sign_up sign_in].map do |link_view|
  23. 896 render(link_view)
  24. end.compact
  25. 224 links.map do |link|
  26. 896 wrap_with_nav_item link
  27. end
  28. end
  29. 1 def self.link_options opts={}
  30. 5 options = { denial: :blank, cache: :never }.merge opts
  31. 979 options[:perms] = ->(r) { yield r } if block_given?
  32. 5 options.clone
  33. end
  34. 1 view :sign_up, link_options(&:show_signup_link?) do
  35. 32 link_to_card :signup, account_link_text(:sign_up),
  36. class: nav_link_class("signup-link"),
  37. path: { action: :new, mark: :signup }
  38. end
  39. 225 view(:sign_in, link_options { !Auth.signed_in? }) do
  40. 22 link_to_card :signin, account_link_text(:sign_in),
  41. class: nav_link_class("signin-link")
  42. end
  43. 225 view(:sign_out, link_options { Auth.signed_in? }) do
  44. 202 link_to_card :signin, account_link_text(:sign_out),
  45. class: nav_link_class("signout-link"),
  46. path: { action: :delete }
  47. end
  48. 1 view :invite, link_options(&:show_invite_link?) do
  49. link_to_card :signup, account_link_text(:invite),
  50. class: nav_link_class("invite-link"),
  51. path: { action: :new, mark: :signup }
  52. end
  53. 225 view(:my_card, link_options { Auth.signed_in? }) do
  54. 202 can_disable_roles? ? interactive_roles_dropdown : simple_roles_dropdown
  55. end
  56. 1 def interactive_roles_dropdown
  57. 190 nest(enabled_roles_card,
  58. view: :edit_inline, hide: %i[edit_inline_buttons name_formgroup])
  59. end
  60. 1 def simple_roles_dropdown
  61. 12 account_dropdown(&method(:link_to_card))
  62. end
  63. 1 def enabled_roles_card
  64. 190 Auth.current.fetch :enabled_roles, new: { type_id: SessionID }
  65. end
  66. 1 def role_list
  67. Auth.current_roles.map(&method(:link_to_card))
  68. end
  69. 1 def can_disable_roles?
  70. 202 Auth.current_roles.size > 1 &&
  71. Card::Codename.exists?(:enabled_roles) # workaround for broken migrations
  72. end
  73. 1 def account_link_text purpose
  74. 256 voo.title ||
  75. I18n.t(purpose, scope: "mod.card-mod-account.set.self.account_links")
  76. end
  77. 1 def nav_link_class type
  78. 256 "nav-link #{classy(type)}"
  79. end
  80. 1 def show_signup_link?
  81. 302 !Auth.signed_in? && Card.new(type_id: SignupID).ok?(:create)
  82. end
  83. 1 def show_invite_link?
  84. Auth.signed_in? &&
  85. Card.new(type_id: Card.default_accounted_type_id).ok?(:create)
  86. end
  87. end
  88. end;end;end;end;
  89. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/self/account_links.rb ~~

card/tmpsets/set/mod013-navbar/self/dropdown_divider.rb

88.89% lines covered

9 relevant lines. 8 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "DropdownDivider"
  4. #
  5. 1 module DropdownDivider;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/self/dropdown_divider.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :nav_item do
  10. wrap_with :div, "", class: "dropdown-divider"
  11. end
  12. 1 view :nav_link_in_dropdown do
  13. 224 wrap_with :div, "", class: "dropdown-divider"
  14. end
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/self/dropdown_divider.rb ~~

card/tmpsets/set/mod013-navbar/self/navbox.rb

100.0% lines covered

18 relevant lines. 18 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Navbox"
  4. #
  5. 1 module Navbox;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/self/navbox.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :navbox, cache: :never do
  10. 224 select_tag "query[keyword]", "", class: "_navbox navbox form-control w-100",
  11. placeholder: navbar_placeholder
  12. end
  13. 1 view :navbar do
  14. # FIXME: not bootstrap class here.
  15. 224 class_up "navbox-form", "form-inline"
  16. 224 render_core
  17. end
  18. 1 view :core do
  19. 224 form_tag path(mark: :search), method: "get", role: "search",
  20. class: classy("navbox-form", "nodblclick") do
  21. 224 wrap_with :div, class: "form-group w-100" do
  22. 224 render_navbox
  23. end
  24. end
  25. end
  26. # def initial_options
  27. # return "" unless (keyword = params.dig :query, :keyword)
  28. # options_for_select [keyword]
  29. # end
  30. # TODO: the more natural placeholder would be the content of the navbox card, no?
  31. # Also, the forced division of "raw" and "core" should probably be replaced
  32. # with a single haml template (for core view)
  33. 1 def navbar_placeholder
  34. 224 @@placeholder ||= begin
  35. 1 holder_card = Card["#{Card[:navbox].name}+*placeholder"]
  36. 1 holder_card ? holder_card.content : "Search"
  37. end
  38. end
  39. end
  40. end;end;end;end;
  41. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/self/navbox.rb ~~

card/tmpsets/set/mod013-navbar/type/html.rb

63.64% lines covered

11 relevant lines. 7 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Html" cards
  4. #
  5. 1 module Html;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card/mod/navbar/set/type/html.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # deprecated; here to support old "*main menu" html cards in existing decks
  10. 1 view :navbar_links, perms: :none do
  11. wrap_with :ul, class: "navbar-nav" do
  12. item_links.map do |link|
  13. wrap_with(:li, class: "nav-item") { link }
  14. end.join "\n"
  15. end
  16. end
  17. 1 def item_links _args={}
  18. raw(render_core).split(/[,\n]/)
  19. end
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/navbar/set/type/html.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/bridge.rb

44.12% lines covered

34 relevant lines. 15 lines covered and 19 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Bridge)
  4. #
  5. 1 module Bridge;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge.rb"; end
  8. 1 BRIDGE_TABS = { "Account" => :account_tab,
  9. "Guide" => :guide_tab,
  10. "Engage" => :engage_tab,
  11. "History" => :history_tab,
  12. "Related" => :related_tab,
  13. "Rules" => :rules_tab }.freeze
  14. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  15. 1 wrapper :bridge do
  16. class_up "modal-dialog", "no-gaps"
  17. voo.hide! :modal_footer
  18. wrap_with_modal size: :full, title: bridge_breadcrumbs do
  19. haml :bridge
  20. end
  21. end
  22. 1 def bridge_tabs
  23. wrap do
  24. tabs(visible_bridge_tabs, bridge_tab, load: :lazy) { _render bridge_tab }
  25. end
  26. end
  27. 1 def bridge_tab
  28. @bridge_tab ||= bridge_param :tab
  29. end
  30. 1 def bridge_param key
  31. params.dig(:bridge, key)&.to_sym || try("default_bridge_#{key}")
  32. end
  33. 1 def bridge_breadcrumbs
  34. <<-HTML.strip_heredoc
  35. <nav aria-label="breadcrumb">
  36. <ol class="breadcrumb _bridge-breadcrumb">
  37. <li class="breadcrumb-item">#{card.name}</li>
  38. <li class="breadcrumb-item active">Edit</li>
  39. </ol>
  40. </nav>
  41. HTML
  42. end
  43. 1 def bridge_link_opts opts={}
  44. opts[:"data-slot-selector"] = bridge_slot_selector
  45. opts[:remote] = true
  46. add_class opts, "slotter"
  47. opts.bury :path, :layout, :overlay
  48. opts[:path][:view] ||= :content
  49. opts
  50. end
  51. 1 def bridge_slot_selector
  52. ".bridge-main > .overlay-container > .card-slot._bottomlay-slot," \
  53. ".bridge-main > ._overlay-container-placeholder > .card-slot"
  54. end
  55. 1 def default_bridge_tab
  56. show_guide_tab? ? :guide_tab : :engage_tab
  57. end
  58. 1 def breadcrumb_data title, html_class=nil
  59. html_class ||= title.underscore
  60. { "data-breadcrumb": title, "data-breadcrumb-class": html_class }
  61. end
  62. end
  63. end;end;end;end;
  64. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/bridge/bridge_pills.rb

38.71% lines covered

31 relevant lines. 12 lines covered and 19 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bridge;
  3. # Set: All cards (Bridge, BridgePills)
  4. #
  5. 1 module BridgePills;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/bridge_pills.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. BRIDGE_PILL_UL_CLASSES =
  10. 1 "nav nav-pills _auto-single-select bridge-pills flex-column".freeze
  11. 1 BRIDGE_PILL_LI_CLASSES = "nav-item".freeze
  12. 1 def bridge_pills items
  13. list_tag class: BRIDGE_PILL_UL_CLASSES, items: { class: BRIDGE_PILL_LI_CLASSES } do
  14. items
  15. end
  16. end
  17. 1 def bridge_pill_items data, breadcrumb
  18. data.map do |text, field, extra_opts|
  19. opts = bridge_pill_item_opts breadcrumb, extra_opts, text
  20. mark = opts.delete(:mark) == :absolute ? field : [card, field]
  21. link_to_card mark, text, opts
  22. end
  23. end
  24. 1 def bridge_pill_item_opts breadcrumb, extra_opts, text
  25. opts = bridge_link_opts.merge("data-toggle": "pill")
  26. opts.merge! breadcrumb_data(breadcrumb)
  27. if extra_opts
  28. classes = extra_opts.delete :class
  29. add_class opts, classes if classes
  30. opts.deep_merge! extra_opts
  31. end
  32. opts["data-cy"] = "#{text.to_name.key}-pill"
  33. add_class opts, "nav-link"
  34. opts
  35. end
  36. 1 def bridge_pill_sections tab_name
  37. wrap_with :ul, class: BRIDGE_PILL_UL_CLASSES do
  38. yield.map { |args| bridge_pill_section(tab_name, *args) }
  39. end
  40. end
  41. 1 def bridge_pill_section tab_name, title, items
  42. wrap_with(:h6, title, class: "ml-1 mt-3") +
  43. wrap_each_with(:li, class: BRIDGE_PILL_LI_CLASSES) do
  44. bridge_pill_items(items, tab_name)
  45. end.html_safe
  46. end
  47. end
  48. end;end;end;end;end;
  49. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/bridge_pills.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/bridge/follow_section.rb

47.62% lines covered

21 relevant lines. 10 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bridge;
  3. # Set: All cards (Bridge, FollowSection)
  4. #
  5. 1 module FollowSection;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/follow_section.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def follow_section
  10. return unless show_follow?
  11. wrap_with :div, class: "mb-3" do
  12. [follow_button_group, followers_bridge_link, follow_overview_button]
  13. end
  14. end
  15. 1 def follow_button_group
  16. wrap_with :div, class: "btn-group btn-group-sm follow-btn-group" do
  17. [follow_button, follow_advanced]
  18. end
  19. end
  20. 1 def follow_overview_button
  21. link_to_card [Auth.current, :follow], "all followed cards",
  22. bridge_link_opts(class: "btn btn-sm btn-secondary",
  23. "data-cy": "follow-overview")
  24. end
  25. 1 def follow_advanced
  26. opts = bridge_link_opts(class: "btn btn-sm btn-primary",
  27. path: { view: :overlay_rule },
  28. "data-cy": "follow-advanced")
  29. opts[:path].delete :layout
  30. link_to_card card.follow_rule_card(Auth.current.name, new: {}),
  31. icon_tag("more_horiz"), opts
  32. end
  33. 1 def followers_bridge_link
  34. cnt = card.followers_count
  35. link_to_card card.name.field(:followers), "#{cnt} follower#{'s' unless cnt == 1}",
  36. bridge_link_opts(class: "btn btn-sm ml-2 btn-secondary slotter",
  37. remote: true, "data-cy": "followers")
  38. end
  39. end
  40. end;end;end;end;end;
  41. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/follow_section.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/bridge/related_section.rb

56.25% lines covered

16 relevant lines. 9 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bridge;
  3. # Set: All cards (Bridge, RelatedSection)
  4. #
  5. 1 module RelatedSection;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/related_section.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. RELATED_ITEMS =
  10. {
  11. 1 "by name" => [["children", :children],
  12. ["mates", :mates]],
  13. # FIXME: optimize,
  14. "by content" => [["links out", :links_to],
  15. ["links in", :linked_to_by],
  16. ["nests", :nests],
  17. ["nested by", :nested_by],
  18. ["references out", :refers_to],
  19. ["references in", :referred_to_by]]
  20. # ["by edit", [["creator", :creator],
  21. # ["editors", :editors],
  22. # ["last edited", :last_edited]]]
  23. }.freeze
  24. 1 def related_by_name_items
  25. pills = []
  26. if card.name.junction?
  27. pills += card.name.ancestors.map { |a| [a, a, { mark: :absolute }] }
  28. end
  29. pills += RELATED_ITEMS["by name"]
  30. pills
  31. end
  32. 1 def related_by_content_items
  33. RELATED_ITEMS["by content"]
  34. end
  35. 1 def related_by_type_items
  36. [["#{card.type} cards", [card.type, :type, :by_name], mark: :absolute]]
  37. end
  38. end
  39. end;end;end;end;end;
  40. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/related_section.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/bridge/tab_views.rb

46.15% lines covered

26 relevant lines. 12 lines covered and 14 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bridge;
  3. # Set: All cards (Bridge, TabViews)
  4. #
  5. 1 module TabViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/tab_views.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :engage_tab, wrap: { div: { class: "m-3 mt-4 _engage-tab" } }, cache: :never do
  10. [render_follow_section, discussion_section].compact
  11. end
  12. 1 view :history_tab, wrap: :slot do
  13. class_up "d0-card-body", "history-slot"
  14. voo.hide :act_legend
  15. acts_bridge_layout card.history_acts
  16. end
  17. 1 view :related_tab do
  18. bridge_pill_sections "Related" do
  19. %w[name content type].map do |section_name|
  20. ["by #{section_name}", send("related_by_#{section_name}_items")]
  21. end
  22. end
  23. end
  24. 1 view :rules_tab, unknown: true do
  25. class_up "card-slot", "flex-column"
  26. wrap do
  27. nest current_set_card, view: :bridge_rules_tab
  28. end
  29. end
  30. 1 view :follow_section, wrap: :slot, cache: :never do
  31. follow_section
  32. end
  33. 1 view :guide_tab, unknown: true do
  34. render_guide
  35. end
  36. 1 def discussion_section
  37. return unless show_discussion?
  38. field_nest(:discussion, view: :titled, title: "Discussion", show: :comment_box,
  39. hide: [:menu])
  40. end
  41. end
  42. end;end;end;end;end;
  43. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/tab_views.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/bridge/tab_visibility.rb

50.0% lines covered

32 relevant lines. 16 lines covered and 16 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bridge;
  3. # Set: All cards (Bridge, TabVisibility)
  4. #
  5. 1 module TabVisibility;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/tab_visibility.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def visible_bridge_tabs
  10. Bridge::BRIDGE_TABS.select do |_title, view|
  11. send "show_#{view}?"
  12. end
  13. end
  14. 1 private
  15. 1 def show_engage_tab?
  16. return unless card.real?
  17. show_follow? || show_discussion?
  18. end
  19. 1 def show_account_tab?
  20. false
  21. end
  22. 1 def show_history_tab?
  23. card.real?
  24. end
  25. 1 def show_related_tab?
  26. card.real?
  27. end
  28. 1 def show_rules_tab?
  29. true
  30. end
  31. 1 def show_guide_tab?
  32. guide.present?
  33. end
  34. 1 def show_discussion?
  35. d_card = discussion_card
  36. return unless d_card
  37. permission_task = d_card.new_card? ? :update : :read
  38. d_card.ok? permission_task
  39. end
  40. 1 def discussion_card?
  41. card.junction? && card.name.tag_name.key == :discussion.cardname.key
  42. end
  43. 1 def discussion_card
  44. return if card.new_card? || discussion_card?
  45. card.fetch :discussion, skip_modules: true, new: {}
  46. end
  47. end
  48. end;end;end;end;end;
  49. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/bridge/tab_visibility.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/edit_content.rb

100.0% lines covered

28 relevant lines. 28 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (EditContent)
  4. #
  5. 1 module EditContent;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_content.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :edit_form, wrap: :slot do
  10. 22 voo.show :edit_type_row
  11. 22 with_nest_mode :edit do
  12. 22 edit_form
  13. end
  14. end
  15. 1 def edit_form
  16. 22 voo.hide :edit_type_row
  17. 22 form_opts = edit_form_opts.reverse_merge success: edit_success
  18. 22 card_form(:update, form_opts) do
  19. [
  20. 22 edit_view_hidden,
  21. _render_edit_type_row(home_view: :edit_type_row),
  22. # home_view is necessary for cancel to work correctly.
  23. # it seems a little strange to have to think about home_view here,
  24. # but the issue is that something currently has to happen prior to the
  25. # render to get voo.slot_options to have the write home view in
  26. # the slot wrap. Id think this would probably best be handled as an
  27. # option to #wrap that triggers a new heir voo
  28. _render_content_formgroups,
  29. _render_edit_buttons
  30. ]
  31. end
  32. end
  33. 1 view :edit, perms: :update, unknown: true, cache: :never,
  34. wrap: { modal: { footer: "",
  35. size: :edit_modal_size,
  36. title: :render_title,
  37. menu: :edit_modal_menu } } do
  38. 22 add_name_context
  39. 22 with_nest_mode :edit do
  40. 22 voo.show :help
  41. 22 voo.hide :save_button
  42. 22 wrap true do
  43. [
  44. 22 frame_help,
  45. _render_edit_form
  46. ]
  47. end
  48. end
  49. end
  50. 1 def edit_modal_size
  51. 22 :large
  52. end
  53. 1 def edit_modal_menu
  54. 22 wrap_with_modal_menu do
  55. 22 [close_modal_window, render_bridge_link]
  56. end
  57. end
  58. 1 def edit_form_opts
  59. # for override
  60. 22 { "data-slot-selector": "modal-origin", "data-slot-error-selector": ".card-slot" }
  61. end
  62. end
  63. end;end;end;end;
  64. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_content.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/edit_inline.rb

75.0% lines covered

28 relevant lines. 21 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (EditInline)
  4. #
  5. 1 module EditInline;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_inline.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :edit_inline, perms: :update, unknown: true, cache: :never, wrap: :slot do
  10. 190 voo.hide :name_formgroup, :type_formgroup
  11. 190 with_nest_mode :edit do
  12. 190 card_form :update, success: edit_success do
  13. [
  14. 190 edit_view_hidden,
  15. _render_content_formgroups,
  16. _render_edit_inline_buttons
  17. ]
  18. end
  19. end
  20. end
  21. 1 view :edit_name_row do
  22. edit_row_fixed_width "Name", card.name, :name_form
  23. end
  24. 1 view :edit_inline_buttons do
  25. button_formgroup do
  26. wrap_with "div", class: "d-flex" do
  27. [standard_save_button, cancel_in_place_button, delete_button]
  28. end
  29. end
  30. end
  31. # TODO: better styling for this so that is reusable
  32. # At the moment it is used for the name and type field in the bridge
  33. # (with fixed 50px width for the title column) and
  34. # for password and email for accounts (with fixed 75px width for the title column)
  35. # The view is very similar to labeled but with fixed edit link on the right
  36. # and a fixed width for the labels so that the content column is aligned
  37. # There is also the problem that label and content are not vertically aligned
  38. 1 view :edit_row do
  39. edit_row_fixed_width render_title, render_core, :edit_inline, 75
  40. end
  41. 1 def edit_row_fixed_width title, content, edit_view, width=50
  42. 22 class_up "card-slot", "d-flex"
  43. 22 wrap do
  44. 22 ["<label class='w-#{width}px'>#{title}</label>",
  45. content,
  46. edit_inline_link(edit_view, align: :right)]
  47. end
  48. end
  49. 1 def edit_inline_link view=:edit_inline, align: :left
  50. 22 align = align == :left ? "ml-2" : "ml-auto"
  51. 22 link_to_view view, menu_icon, class: "#{align} edit-link", "data-cy": "edit-link"
  52. end
  53. 1 def cancel_in_place_button args={}
  54. args.reverse_merge! class: "cancel-button btn-sm", href: path
  55. cancel_button args
  56. end
  57. end
  58. end;end;end;end;
  59. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_inline.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/edit_name.rb

45.16% lines covered

31 relevant lines. 14 lines covered and 17 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (EditName)
  4. #
  5. 1 module EditName;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_name.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # note: depends on js with selector ".edit_name-view .card-form"
  10. 1 view :edit_name, perms: :update do
  11. frame { name_form }
  12. end
  13. # note: depends on js with selector ".name_form-view .card-form"
  14. 1 view :name_form, perms: :update, wrap: :slot, cache: :never do
  15. name_form :edit_name_row
  16. end
  17. 1 def name_form success_view=nil
  18. card_form({ action: :update, id: card.id },
  19. # "main-success" => "REDIRECT",
  20. "data-update-origin": "true",
  21. success: edit_name_success(success_view)) do
  22. [hidden_edit_name_fields,
  23. _render_name_formgroup,
  24. rename_confirmation_alert,
  25. edit_name_buttons]
  26. end
  27. end
  28. 1 def edit_name_success view=nil
  29. success = { id: "_self" }
  30. success[:view] = view if view
  31. success
  32. end
  33. 1 def hidden_edit_name_fields
  34. hidden_tags old_name: card.name, card: { update_referers: false }
  35. end
  36. 1 def edit_name_buttons
  37. button_formgroup do
  38. [rename_and_update_button, rename_button, standard_cancel_button]
  39. end
  40. end
  41. # LOCALIZE
  42. 1 def rename_and_update_button
  43. submit_button text: "Rename and Update", disable_with: "Renaming",
  44. class: "renamer-updater"
  45. end
  46. 1 def rename_button
  47. button_tag "Rename", data: { disable_with: "Renaming" }, class: "renamer"
  48. end
  49. # LOCALIZE
  50. 1 def rename_confirmation_alert
  51. msg = "<h5>Are you sure you want to rename <em>#{safe_name}</em>?</h5>"
  52. msg << %(<h6>This may change names referred to by other cards.</h6>)
  53. msg << %(<p>You may choose to <em>update or ignore</em> the referers.</p>)
  54. msg << hidden_field_tag(:referers, 1)
  55. alert("warning", false, false, class: "hidden-alert") { msg }
  56. end
  57. end
  58. end;end;end;end;
  59. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_name.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/edit_type.rb

77.78% lines covered

54 relevant lines. 42 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (EditType)
  4. #
  5. 1 module EditType;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_type.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :edit_type, cache: :never, perms: :update do
  10. frame do
  11. _render_edit_type_form
  12. end
  13. end
  14. 1 view :edit_type_form, cache: :never, perms: :update, wrap: :slot do
  15. card_form :update, success: edit_type_success do
  16. [type_formgroup, render_new_buttons]
  17. end
  18. end
  19. 1 def edit_type_success
  20. { view: :core }
  21. end
  22. 1 view :edit_type_row do
  23. 44 return _render_bridge_type_formgroup if voo.visible?(:type_form) { false }
  24. 22 edit_row_fixed_width "Type", link_to_card(card.type), :bridge_type_formgroup
  25. end
  26. 1 view :bridge_type_formgroup, unknown: true, wrap: :slot do
  27. type_formgroup href: path(mark: card.id,
  28. view: :edit_form,
  29. assign: true,
  30. slot: { show: :type_form }),
  31. class: "live-type-field slotter",
  32. 'data-remote': true,
  33. 'data-slot-selector': ".card-slot.edit_form-view"
  34. end
  35. 1 view :type_formgroup do
  36. type_formgroup
  37. end
  38. 1 def type_formgroup args={}
  39. add_class args, "type-field"
  40. wrap_type_formgroup do
  41. type_field args
  42. end
  43. end
  44. 1 def wrap_type_formgroup
  45. 4 formgroup "Type", input: "type", class: "type-formgroup", help: false do
  46. 4 output [yield, hidden_field_tag(:assign, true)]
  47. end
  48. end
  49. 1 def type_field args={}
  50. 4 typelist = Auth.createable_types
  51. 4 current_type = type_field_current_value args, typelist
  52. 4 action_view.select_tag "card[type]", type_field_options(current_type),
  53. args.merge("data-select2-id": "#{unique_id}-#{Time.now.to_i}")
  54. end
  55. 1 def type_field_options current_type
  56. 4 types = grouped_types(current_type)
  57. 4 if types.size == 1
  58. options_for_select types.flatten[1], current_type
  59. else
  60. 4 grouped_options_for_select types, current_type
  61. end
  62. end
  63. 1 def grouped_types current_type
  64. 40 groups = Hash.new { |h, k| h[k] = [] }
  65. 4 allowed = ::Set.new Auth.createable_types
  66. 4 allowed << current_type if current_type
  67. 4 visible_cardtype_groups.each_pair do |name, items|
  68. 36 if name == "Custom"
  69. 4 Auth.createable_types.each do |type|
  70. 177 groups["Custom"] << type unless ::Card::Set::Self::Cardtype::GROUP_MAP[type]
  71. end
  72. else
  73. 32 items.each do |i|
  74. 148 groups[name] << i if allowed.include?(i)
  75. end
  76. end
  77. end
  78. 4 groups
  79. end
  80. 1 def visible_cardtype_groups
  81. 4 ::Card::Set::Self::Cardtype::GROUP
  82. end
  83. 1 def type_field_current_value args, typelist
  84. 4 return if args.delete :no_current_type
  85. 4 if !card.new_card? && !typelist.include?(card.type_name)
  86. # current type should be an option on existing cards,
  87. # regardless of create perms
  88. typelist.push(card.type_name).sort!
  89. end
  90. 4 card.type_name_or_default
  91. end
  92. end
  93. end;end;end;end;
  94. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/edit_type.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/editing.rb

56.25% lines covered

32 relevant lines. 18 lines covered and 14 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Editing)
  4. #
  5. 1 module Editing;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/editing.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. ###---( TOP_LEVEL (used by menu) NEW / EDIT VIEWS )
  10. 1 view :bridge, perms: :update, unknown: true, cache: :never, wrap: :bridge do
  11. with_nest_mode :edit do
  12. add_name_context
  13. voo.show :help
  14. wrap true, breadcrumb_data("Editing", "edit") do
  15. bridge_parts
  16. end
  17. end
  18. end
  19. 1 view :cardboard, :bridge
  20. 1 def bridge_parts
  21. voo.show! :edit_type_row
  22. [
  23. frame_help,
  24. _render_edit_name_row(home_view: :edit_name_row),
  25. # home_view is necessary for cancel to work correctly.
  26. # it seems a little strange to have to think about home_view here,
  27. # but the issue is that something currently has to happen prior to the
  28. # render to get voo.slot_options to have the write home view in
  29. # the slot wrap. I think this would probably best be handled as an
  30. # option to #wrap that triggers a new heir voo
  31. _render_edit_form
  32. ]
  33. end
  34. 1 def edit_success
  35. # for override
  36. end
  37. 1 def edit_view_hidden
  38. # for override
  39. end
  40. 1 view :edit_buttons do
  41. 21 button_formgroup do
  42. 21 wrap_with "div", class: "d-flex" do
  43. 21 [standard_submit_button, edit_cancel_button, delete_button]
  44. end
  45. end
  46. end
  47. # TODO: add undo functionality
  48. 1 view :just_deleted, unknown: true do
  49. wrap { "#{render_title} deleted" }
  50. end
  51. 1 view :edit_rules, cache: :never, unknown: true do
  52. nest current_set_card, view: :bridge_rules_tab
  53. end
  54. 1 view :edit_structure, cache: :never do
  55. return unless card.structure
  56. nest card.structure_rule_card, view: :edit
  57. # FIXME: this stuff:
  58. # slot: {
  59. # cancel_slot_selector: ".card-slot.related-view",
  60. # cancel_path: card.format.path(view: :edit), hide: :edit_toolbar,
  61. # hidden: { success: { view: :open, "slot[subframe]" => true } }
  62. # }
  63. # }
  64. end
  65. 1 view :edit_nests, cache: :never do
  66. frame do
  67. with_nest_mode :edit do
  68. multi_card_edit
  69. end
  70. end
  71. end
  72. # FIXME: - view can recurse. temporarily turned off
  73. #
  74. # view :edit_nest_rules, cache: :never do
  75. # return ""#
  76. # view = args[:rule_view] || :field_related_rules
  77. # frame do
  78. # # with_nest_mode :edit do
  79. # nested_fields.map do |name, _options|
  80. # nest Card.fetch(name.to_name.trait(:self)),
  81. # view: :titled, title: name, rule_view: view,
  82. # hide: :set_label, show: :rule_navbar
  83. # end
  84. # end
  85. # end
  86. end
  87. end;end;end;end;
  88. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/editing.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/editor.rb

90.0% lines covered

30 relevant lines. 27 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Editor)
  4. #
  5. 1 module Editor;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/editor.rb"; end
  8. 1 Self::InputOptions.add_to_basket :options, "text area"
  9. 1 Self::InputOptions.add_to_basket :options, "text field"
  10. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  11. 1 def input_type
  12. 116 voo.input_type.present? ? voo.input_type : input_type_from_rule
  13. end
  14. 1 def input_type_from_rule
  15. 116 card.rule(:input_type)&.gsub(/[\[\]]/, "")&.tr(" ", "_")
  16. end
  17. 1 def input_method input_type
  18. 409 "#{input_type}_input"
  19. end
  20. # core view of card is input
  21. 1 def input_defined_by_card
  22. 56 with_card input_type do |input_card|
  23. nest input_card, view: :core
  24. end
  25. end
  26. # move somewhere more accessible?
  27. 1 def with_card mark
  28. 56 return nil unless (card = Card[mark])
  29. yield card
  30. rescue Card::Error::CodenameNotFound
  31. nil
  32. end
  33. 1 view :input, unknown: true do
  34. 353 try(input_method(input_type)) ||
  35. input_defined_by_card ||
  36. send(input_method(default_input_type))
  37. end
  38. 1 def default_input_type
  39. 56 :rich_text
  40. end
  41. 1 def rich_text_input
  42. 56 send "#{Cardio.config.rich_text_editor || :text_area}_editor_input"
  43. end
  44. 1 def text_area_input
  45. 1 text_area :content, rows: 5, class: "d0-card-content",
  46. "data-card-type-code" => card.type_code
  47. end
  48. 1 def text_field_input
  49. 99 text_field :content, class: classy("d0-card-content")
  50. end
  51. end
  52. end;end;end;end;
  53. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/editor.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/form.rb

94.21% lines covered

121 relevant lines. 114 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Form)
  4. #
  5. 1 module Form;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/form.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # FIELDSET VIEWS
  10. # sometimes multiple card formgroups, sometimes just one
  11. 1 view :content_formgroups, cache: :never do
  12. 341 wrap_with :fieldset, edit_slot, class: classy("card-editor", "editor")
  13. end
  14. 1 view :name_formgroup do
  15. 12 formgroup "Name", input: "name", help: false do
  16. 12 raw name_field
  17. end
  18. end
  19. # single card content formgroup, labeled with "Content"
  20. 1 view :content_formgroup, unknown: true, cache: :never do
  21. 82 wrap_content_formgroup { content_field }
  22. end
  23. 1 view :edit_in_form, cache: :never, perms: :update, unknown: true do
  24. 214 reset_form
  25. 214 @in_multi_card_editor = true
  26. 214 edit_slot
  27. end
  28. 1 view :conflict_tracker, cache: :never, unknown: true do
  29. 445 return unless card&.real?
  30. 27 card.last_action_id_before_edit = card.last_action_id
  31. 27 hidden_field :last_action_id_before_edit, class: "current_revision_id"
  32. end
  33. 1 def wrap_content_formgroup
  34. 41 formgroup("Content", input: :content, help: false,
  35. 41 class: classy("card-editor")) { yield }
  36. end
  37. 1 def button_formgroup
  38. 150 wrap_with :div, class: classy("form-group") do
  39. 150 wrap_with :div, yield
  40. end
  41. end
  42. 1 def name_field
  43. # value needed because otherwise gets wrong value if there are updates
  44. 12 text_field :name, value: card.name, autocomplete: "off"
  45. end
  46. 1 def content_field
  47. 445 with_nest_mode :normal do
  48. # by changing nest mode to normal, we ensure that editors (eg image
  49. # previews) can render core views.
  50. 445 output [_render_conflict_tracker, _render_input]
  51. end
  52. end
  53. # SAMPLE editor view for override
  54. # view :input do
  55. # text_area :content, rows: 5, class: "d0-card-content"
  56. # end
  57. 1 def edit_slot
  58. case
  59. 555 when inline_nests_editor? then _render_core
  60. 110 when multi_card_editor? then multi_card_edit(true)
  61. 214 when in_multi_card_editor? then editor_in_multi_card
  62. 231 else single_card_edit_field
  63. end
  64. end
  65. # test: render nests within a normal rendering of the card's content?
  66. # (as opposed to a standardized form)
  67. 1 def inline_nests_editor?
  68. 555 voo.input_type == :inline_nests
  69. end
  70. # test: are we opening a new multi-card form?
  71. 1 def multi_card_editor?
  72. 555 voo.structure || voo.edit_structure || # structure configured in voo
  73. card.structure || # structure in card rule
  74. edit_fields? # list of fields in card rule
  75. end
  76. # override and return true to optimize
  77. 1 def edit_fields?
  78. 445 edit_fields.present?
  79. end
  80. # test: are we already within a multi-card form?
  81. 1 def in_multi_card_editor?
  82. 445 @in_multi_card_editor.present?
  83. end
  84. 1 def single_card_edit_field
  85. 231 if voo.show?(:type_formgroup) || voo.show?(:name_formgroup)
  86. 41 _render_content_formgroup # use formgroup for consistency
  87. else
  88. 380 editor_wrap(:content) { content_field }
  89. end
  90. end
  91. 1 def editor_in_multi_card
  92. 214 add_junction_class
  93. 214 formgroup render_title,
  94. input: "content", help: true, class: classy("card-editor") do
  95. 214 [content_field, (form.hidden_field(:type_id) if card.new_card?)]
  96. end
  97. end
  98. 1 def multi_card_edit fields_only=false
  99. 110 field_configs = edit_field_configs fields_only
  100. 110 return structure_link if field_configs.empty?
  101. 110 field_configs.map do |name, options|
  102. 214 nest name, options || {}
  103. end.join "\n"
  104. end
  105. 1 def structure_link
  106. # LOCALIZE
  107. structured = link_to_card card.structure_rule_card, "structured"
  108. "<label>Content</label>"\
  109. "<p><em>Uneditable; content is #{structured} without nests</em></p>"
  110. end
  111. # @param [Hash|Array] fields either an array with field names and/or field
  112. # cards or a hash with the fields as keys and a hash with nest options as
  113. # values
  114. 1 def process_edit_fields fields
  115. fields.map do |field, opts|
  116. field_nest field, opts
  117. end.join "\n"
  118. end
  119. ###
  120. # If you use subfield cards to render a form for a new card
  121. # then the subfield cards should be created on the new card not the existing
  122. # card that build the form
  123. 1 def form
  124. 954 @form ||= inherit(:form) || new_form
  125. end
  126. 1 def new_form
  127. 418 @form_root = true unless parent&.form_root
  128. 418 instantiate_builder(form_prefix, card, {})
  129. end
  130. 1 def reset_form
  131. 214 @form = new_form
  132. end
  133. 1 def form_prefix
  134. case
  135. 636 when explicit_form_prefix then explicit_form_prefix # configured
  136. 418 when simple_form? then "card" # simple form
  137. when parent.card.name == card.name then parent.form_prefix # card nests self
  138. 218 else edit_in_form_prefix
  139. end
  140. end
  141. 1 def simple_form?
  142. 636 form_root? || !form_root || !parent
  143. end
  144. 1 def edit_in_form_prefix
  145. 218 "#{parent.form_prefix}[subcards][#{card.name.from form_context.card.name}]"
  146. end
  147. 1 def explicit_form_prefix
  148. 636 inherit :explicit_form_prefix
  149. end
  150. 1 def form_context
  151. 218 form_root? || !form_root ? self : parent
  152. end
  153. 1 def form_root?
  154. 854 @form_root == true
  155. end
  156. 1 def form_root
  157. 1560 return self if @form_root
  158. 838 parent ? parent.form_root : nil
  159. end
  160. 1 def card_form action, opts={}
  161. 341 @form_root = true
  162. 341 hidden = hidden_form_tags action, opts
  163. 341 form_for card, card_form_opts(action, opts) do |cform|
  164. 341 @form = cform
  165. 341 hidden + output(yield(cform))
  166. end
  167. end
  168. 1 def hidden_form_tags _action, opts
  169. 341 success = opts.delete :success
  170. 341 success_tags success
  171. end
  172. # @param action [Symbol] :create or :update
  173. # @param opts [Hash] html options
  174. # @option opts [Boolean] :redirect (false) if true form is no "slotter"
  175. 1 def card_form_opts action, opts={}
  176. 341 url, action = card_form_url_and_action action
  177. 341 html_opts = card_form_html_opts action, opts
  178. 341 form_opts = { url: url, html: html_opts }
  179. 341 form_opts[:remote] = true unless html_opts.delete(:redirect)
  180. 341 form_opts
  181. end
  182. 1 def card_form_html_opts action, opts={}
  183. 341 add_class opts, "card-form"
  184. 341 add_class opts, "slotter" unless opts[:redirect] || opts[:no_slotter]
  185. 341 add_class opts, "autosave" if action == :update
  186. 341 opts
  187. end
  188. 1 def card_form_url_and_action action
  189. 341 case action
  190. 341 when Symbol then [path(action: action), action]
  191. when Hash then [path(action), action[:action]]
  192. # for when non-action path args are required
  193. else
  194. raise Card::Error, "unsupported #card_form_url action: #{action}"
  195. end
  196. end
  197. 1 def editor_wrap type=nil
  198. 461 html_class = "editor"
  199. 461 html_class << " #{type}-editor" if type
  200. 461 wrap_with :div, class: html_class do
  201. 461 yield
  202. end
  203. end
  204. # FIELD VIEWS
  205. 1 def add_junction_class
  206. 214 return unless card.name.junction?
  207. 214 class_up "card-editor", "RIGHT-#{card.name.tag_name.safe_key}"
  208. end
  209. end
  210. end;end;end;end;
  211. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/form.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/form_buttons.rb

88.1% lines covered

42 relevant lines. 37 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (FormButtons)
  4. #
  5. 1 module FormButtons;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/form_buttons.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def standard_submit_button
  10. 21 output [standard_save_button, standard_save_and_close_button]
  11. end
  12. 1 def standard_save_button opts={}
  13. 21 return if voo&.hide?(:save_button)
  14. add_class opts, "submit-button btn-sm mr-3 _update-history-pills"
  15. opts[:text] ||= "Save"
  16. opts["data-cy"] = "save"
  17. submit_button opts
  18. end
  19. # @param opts [Hash]
  20. # @option close [:modal, :overlay]
  21. #
  22. 1 def standard_save_and_close_button opts={}
  23. 21 close = opts.delete(:close) || :modal
  24. 21 text = opts[:text] || "Save and Close"
  25. 21 add_class opts, "submit-button btn-sm mr-3 _close-on-success"
  26. 21 add_class opts, "_update-origin" unless opts[:no_origin_update]
  27. 21 opts.reverse_merge! text: text, "data-cy": "submit-#{close}"
  28. 21 submit_button opts
  29. end
  30. 1 def standard_cancel_button args={}
  31. 49 args.reverse_merge! class: "cancel-button ml-4", href: path, "data-cy": "cancel"
  32. 49 cancel_button args
  33. end
  34. 1 def modal_cancel_button
  35. 21 modal_close_button "Cancel", situation: "secondary", class: "btn-sm cancel-button"
  36. end
  37. 1 def edit_cancel_button
  38. 21 modal_cancel_button
  39. end
  40. 1 def new_cancel_button
  41. voo.show?(:cancel_button) && modal_cancel_button
  42. end
  43. 1 def delete_button opts={}
  44. 21 link_to "Delete", delete_button_opts(opts)
  45. end
  46. 1 def delete_button_opts opts={}
  47. 21 add_class opts, "slotter btn btn-outline-danger ml-auto btn-sm"
  48. 21 opts["data-confirm"] = delete_confirm opts
  49. 21 opts[:path] = { action: :delete }
  50. 21 opts[:path][:success] = delete_success(opts) unless opts.delete(:no_success)
  51. 21 opts[:remote] = true
  52. 21 opts
  53. end
  54. 1 def delete_confirm opts
  55. 19 opts.delete(:confirm) || "Are you sure you want to delete #{safe_name}?"
  56. end
  57. 1 def delete_success opts
  58. 21 opts.delete(:success) || (main? ? "REDIRECT: *previous" : { view: :just_deleted })
  59. end
  60. end
  61. end;end;end;end;
  62. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/form_buttons.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/form_elements.rb

92.86% lines covered

42 relevant lines. 39 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (FormElements)
  4. #
  5. 1 module FormElements;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/form_elements.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def success_tags opts
  10. 341 return "" unless opts.present?
  11. 319 hidden_tags success: opts
  12. end
  13. # convert hash into a collection of hidden tags
  14. 1 def hidden_tags hash, base=nil
  15. 893 hash ||= {}
  16. 893 hash.inject("") do |result, (key, val)|
  17. 895 new_base = base ? "#{base}[#{key}]" : key
  18. 895 result + process_hidden_value(val, new_base)
  19. end
  20. end
  21. 1 def process_hidden_value val, base
  22. 895 case val
  23. when Hash
  24. 382 hidden_tags val, base
  25. when Array
  26. base += "[]"
  27. val.map do |v|
  28. hidden_field_tag base, v
  29. end.join
  30. else
  31. 513 hidden_field_tag base, val
  32. end
  33. end
  34. FIELD_HELPERS =
  35. %w[
  36. 1 hidden_field color_field date_field datetime_field datetime_local_field
  37. email_field month_field number_field password_field phone_field
  38. range_field search_field telephone_field text_area text_field time_field
  39. url_field week_field file_field label check_box radio_button
  40. ].freeze
  41. 1 FIELD_HELPERS.each do |method_name|
  42. 22 define_method(method_name) do |*args|
  43. 1 form.send(method_name, *args)
  44. end
  45. end
  46. 1 def submit_button args={}
  47. 72 text = args.delete(:text) || "Submit"
  48. 72 args.reverse_merge! situation: "primary", data: {}
  49. 72 args[:data][:disable_with] ||= args.delete(:disable_with) || "Submitting"
  50. 72 button_tag text, args
  51. end
  52. # redirect to *previous if no :href is given
  53. 1 def cancel_button args={}
  54. 49 return unless voo.show? :cancel_button
  55. 49 text = args.delete(:text) || "Cancel"
  56. 49 add_class args, "btn btn-#{args.delete(:situation) || 'secondary'}"
  57. 49 add_class args, cancel_strategy(args[:redirect], args[:href])
  58. 49 args[:href] ||= path_to_previous
  59. 49 args["data-remote"] = true
  60. 49 link_to text, args
  61. end
  62. 1 def cancel_strategy redirect, href
  63. 49 redirect = href.blank? if redirect.nil?
  64. 49 redirect ? "redirecter" : "slotter"
  65. end
  66. 1 def path_to_previous
  67. 49 path mark: "*previous"
  68. end
  69. end
  70. end;end;end;end;
  71. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/form_elements.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/formgroup.rb

100.0% lines covered

27 relevant lines. 27 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Formgroup)
  4. #
  5. 1 module Formgroup;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/formgroup.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # a formgroup has a label, an input and help text
  10. 1 def formgroup title, opts={}, &block
  11. 271 wrap_with :div, formgroup_div_args(opts[:class]) do
  12. 271 formgroup_body title, opts, &block
  13. end
  14. end
  15. 1 def formgroup_body title, opts, &block
  16. 271 label = formgroup_label opts[:input], title
  17. 271 editor_body = editor_wrap opts[:input], &block
  18. 271 help_text = formgroup_help_text opts[:help]
  19. 271 "#{label}<div>#{help_text} #{editor_body}</div>"
  20. end
  21. 1 def formgroup_label input, title
  22. 271 return if voo&.hide?(:title) || title.blank?
  23. 271 label_type = input || :content
  24. 271 form.label label_type, title
  25. end
  26. 1 def formgroup_div_args html_class
  27. 271 div_args = { class: ["form-group", html_class].compact.join(" ") }
  28. 271 div_args[:card_id] = card.id if card.real?
  29. 271 div_args[:card_name] = h card.name if card.name.present?
  30. 271 div_args
  31. end
  32. 1 def formgroup_help_text text=nil
  33. 271 return "" if text == false
  34. 214 class_up "help-text", "help-block"
  35. 214 voo.help = text if voo && text.to_s != "true"
  36. 214 _render_help
  37. end
  38. end
  39. end;end;end;end;
  40. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/formgroup.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/new.rb

79.79% lines covered

94 relevant lines. 75 lines covered and 19 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (New)
  4. #
  5. 1 module New;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/new.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :new, perms: :create, unknown: true, cache: :never do
  10. 50 new_view_frame_and_form new_form_opts
  11. end
  12. 1 view :new_content_form, wrap: :slot, unknown: true, cache: :never do
  13. with_nest_mode :edit do
  14. create_form
  15. end
  16. end
  17. 1 view :new_in_modal, perms: :create, unknown: true, cache: :never,
  18. wrap: { modal: { footer: "", size: :edit_modal_size,
  19. title: :new_in_modal_title,
  20. menu: :new_modal_menu } } do
  21. _render_new_content_form
  22. end
  23. 1 def create_form
  24. form_opts = new_in_modal_form_opts.reverse_merge(success: new_in_modal_success)
  25. buttons = form_opts.delete(:buttons) || _render_new_buttons
  26. voo.title ||= new_view_title if new_name_prompt?
  27. voo.show :help
  28. card_form(:create, form_opts) do
  29. create_form_with_alert_guide buttons
  30. end
  31. end
  32. 1 def new_modal_size
  33. :large
  34. end
  35. 1 def new_modal_menu
  36. wrap_with_modal_menu do
  37. [close_modal_window, render_bridge_link]
  38. end
  39. end
  40. 1 def new_view_frame_and_form form_opts={}
  41. 50 buttons = form_opts.delete(:buttons) || _render_new_buttons
  42. 50 form_opts = form_opts.reverse_merge(success: new_success)
  43. 50 with_nest_mode :edit do
  44. 50 voo.title ||= new_view_title if new_name_prompt?
  45. 50 voo.show :help
  46. 50 frame_and_form :create, form_opts do
  47. 50 create_form_with_alert_guide buttons
  48. end
  49. end
  50. end
  51. 1 def create_form_with_alert_guide buttons
  52. 50 wrap_with :div, class: "d-flex justify-content-between" do
  53. 50 [(wrap_with(:div, class: "w-100") do
  54. [
  55. 50 new_view_hidden,
  56. new_view_name,
  57. new_view_type,
  58. _render_content_formgroups,
  59. buttons
  60. ]
  61. end),
  62. 50 (alert_guide if voo.show?(:guide))]
  63. end
  64. end
  65. 1 def new_view_hidden; end
  66. 1 def new_in_modal_form_opts
  67. { "data-slot-selector": "modal-origin", "data-slot-error-selector": ".card-slot",
  68. buttons: _render_new_in_modal_buttons }
  69. end
  70. 1 def new_form_opts
  71. 50 { "main-success" => "REDIRECT" }
  72. end
  73. 1 def new_view_title
  74. 10 output(
  75. "New",
  76. 10 (card.type_name unless card.type_id == Card.default_type_id)
  77. )
  78. end
  79. 1 def new_in_modal_title
  80. new_name_prompt? ? new_view_title : render_title
  81. end
  82. 1 def new_success
  83. 50 card.rule(:thanks) || "_self"
  84. end
  85. 1 def new_in_modal_success; end
  86. # NAME HANDLING
  87. 1 def new_view_name
  88. 50 if new_name_prompt?
  89. 11 new_name_formgroup
  90. 39 elsif !autoname?
  91. 39 hidden_field_tag "card[name]", card.name
  92. end
  93. end
  94. 1 def new_name_formgroup
  95. 11 output _render_name_formgroup,
  96. hidden_field_tag("name_prompt", true)
  97. end
  98. 1 def new_name_prompt?
  99. 100 voo.visible? :name_formgroup do
  100. 50 needs_name? || params[:name_prompt]
  101. end
  102. end
  103. 1 def autoname?
  104. 50 @autoname.nil? ? (@autoname = card.rule_card :autoname).present? : @autoname
  105. end
  106. 1 def needs_name?
  107. 50 card.name.blank? && !autoname?
  108. end
  109. # TYPE HANDLING
  110. 1 def new_view_type
  111. 50 if new_type_prompt?
  112. 4 _render_new_type_formgroup
  113. else
  114. 46 hidden_field_tag "card[type_id]", card.type_id
  115. end
  116. end
  117. 1 def new_type_prompt?
  118. 50 voo.visible? :new_type_formgroup do
  119. 50 !new_type_preset? && new_type_prompt_context? && new_type_permitted?
  120. end
  121. end
  122. 1 def new_type_preset?
  123. 50 params[:type] || voo.type
  124. end
  125. 1 def new_type_prompt_context?
  126. 4 main? || card.simple? || card.is_template?
  127. end
  128. 1 def new_type_permitted?
  129. 4 Card.new(type_id: card.type_id).ok? :create
  130. end
  131. 1 view :new_type_formgroup do
  132. 4 wrap_type_formgroup do
  133. 4 type_field class: "type-field live-type-field",
  134. href: path(view: :new),
  135. "data-remote" => true
  136. end
  137. end
  138. 1 view :new_buttons do
  139. 49 button_formgroup do
  140. 49 [standard_create_button, standard_cancel_button(cancel_button_new_args)]
  141. end
  142. end
  143. 1 view :new_in_modal_buttons do
  144. button_formgroup do
  145. wrap_with "div", class: "d-flex" do
  146. [standard_save_and_close_button(text: "Submit"), modal_cancel_button]
  147. end
  148. end
  149. end
  150. # path to redirect to after canceling a new form
  151. 1 def cancel_button_new_args
  152. href = case
  153. 98 when main? then path_to_previous
  154. when voo&.home_view then path(view: voo.home_view)
  155. else path(view: :unknown)
  156. end
  157. 49 { href: href }
  158. end
  159. 1 def standard_create_button
  160. 50 submit_button class: "submit-button create-submit-button"
  161. end
  162. end
  163. end;end;end;end;
  164. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/new.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/overlay_guide.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (OverlayGuide)
  4. #
  5. 1 module OverlayGuide;
  6. 1 extend Card::Set
  7. 2 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/overlay_guide.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :overlay_guide,
  10. cache: :never, unknown: true, template: :haml,
  11. wrap: { slot: { class: "_overlay d0-card-overlay card nodblclick" } } do
  12. # TODO: use a common template for this and the nest editor
  13. # (the common thing is that they both are an overlay of the bridge sidebar)
  14. # and maybe make it look more like the overlay on the left with the same close icon
  15. end
  16. end
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/overlay_guide.rb ~~

card/tmpsets/set/mod014-card-mod-edit/all/template_nest.rb

88.89% lines covered

27 relevant lines. 24 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (TemplateNest)
  4. #
  5. 1 module TemplateNest;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/all/template_nest.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 view :template_nest, cache: :never, unknown: true do
  10. 21 return "" unless voo.nest_name
  11. 21 if voo.nest_name.to_name.field_only?
  12. 21 with_nest_mode :normal do
  13. 21 nest template_link_set_name, view: :template_link
  14. end
  15. else
  16. "{{#{voo.nest_syntax}}}"
  17. end
  18. end
  19. 1 def template_link_set_name
  20. 21 name = voo.nest_name.to_name
  21. 21 if name.absolute?
  22. name.trait_name :self
  23. else
  24. 21 template_link_set_name_for_relative_name name
  25. end
  26. end
  27. 1 def template_link_set_name_for_relative_name name
  28. 21 name = name.stripped.gsub(/^\+/, "")
  29. 21 if (type = on_type_set)
  30. 21 [type, name].to_name.trait_name :type_plus_right
  31. else
  32. name.to_name.trait_name :right
  33. end
  34. end
  35. 1 def on_type_set
  36. return unless
  37. 21 (tmpl_set_name = parent.card.name.trunk_name) &&
  38. 21 (tmpl_set_class_name = tmpl_set_name.tag_name) &&
  39. 21 (tmpl_set_class_card = Card[tmpl_set_class_name]) &&
  40. 21 (tmpl_set_class_card.codename == :type)
  41. 21 tmpl_set_name.left_name
  42. end
  43. end
  44. end;end;end;end;
  45. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/all/template_nest.rb ~~

card/tmpsets/set/mod014-card-mod-edit/type/list.rb

66.67% lines covered

12 relevant lines. 8 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "List" cards
  4. #
  5. 1 module List;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/type/list.rb"; end
  8. 1 def input_type_content_options
  9. ["multiselect", "checkbox", "autocompleted list", "filtered list"]
  10. end
  11. 1 def show_content_options?
  12. true
  13. end
  14. 1 def show_input_type?
  15. true
  16. end
  17. 1 def field_settings
  18. %i[default help input_type content_options content_option_view]
  19. end
  20. end;end;end;end;
  21. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/type/list.rb ~~

card/tmpsets/set/mod014-card-mod-edit/type/plain_text.rb

72.73% lines covered

11 relevant lines. 8 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "PlainText" cards
  4. #
  5. 1 module PlainText;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/type/plain_text.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def input_type_content_options
  10. ["text area", "text field", "ace editor"]
  11. end
  12. end
  13. 1 def field_settings
  14. %i[default help input_type]
  15. end
  16. 1 def show_input_type?
  17. true
  18. end
  19. end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/type/plain_text.rb ~~

card/tmpsets/set/mod014-card-mod-edit/type/pointer.rb

70.0% lines covered

10 relevant lines. 7 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Pointer" cards
  4. #
  5. 1 module Pointer;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-edit/set/type/pointer.rb"; end
  8. 1 def show_content_options?
  9. true
  10. end
  11. 1 def show_input_type?
  12. true
  13. end
  14. 1 def input_type_content_options
  15. %w[select radio autocomplete]
  16. end
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-edit/set/type/pointer.rb ~~

card/tmpsets/set/mod015-card-mod-ace_editor/all/ace_editor.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (AceEditor)
  4. #
  5. 1 module AceEditor;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-ace_editor/set/all/ace_editor.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def ace_editor_input
  10. 3 text_area :content, rows: 5,
  11. class: "d0-card-content ace-editor-textarea",
  12. "data-ace-mode" => ace_mode
  13. end
  14. 1 def ace_mode
  15. 3 :html
  16. end
  17. end
  18. end;end;end;end;
  19. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-ace_editor/set/all/ace_editor.rb ~~

card/tmpsets/set/mod015-card-mod-ace_editor/self/ace.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Ace"
  4. #
  5. 1 module Ace;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/ace.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def raw_help_text
  10. "Configure [[https://ace.c9.io/|ace]], "\
  11. "Decko's default code editor, using these available "\
  12. "[[https://github.com/ajaxorg/ace/wiki/Configuring-Ace|options]]."
  13. end
  14. end
  15. end;end;end;end;
  16. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/ace.rb ~~

card/tmpsets/set/mod015-card-mod-ace_editor/self/script_ace.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptAce"
  4. #
  5. 1 module ScriptAce;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/script_ace.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptLibraries.add_item :script_ace
  10. 1 Self::InputOptions.add_to_basket :options, "ace editor"
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/script_ace.rb ~~

card/tmpsets/set/mod015-card-mod-ace_editor/self/script_ace_config.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptAceConfig"
  4. #
  5. 1 module ScriptAceConfig;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/script_ace_config.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptEditors.add_item :script_ace_config
  10. 1 All::Head::HtmlFormat.add_to_basket :mod_js_config, [:ace, "setAceConfig"]
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-ace_editor/set/self/script_ace_config.rb ~~

card/tmpsets/set/mod016-card-mod-bar_and_box/abstract/media.rb

100.0% lines covered

14 relevant lines. 14 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Media)
  4. #
  5. 1 module Media;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/abstract/media.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def image_card
  10. 7 @image_card ||= card.fetch(:image, new: {})
  11. end
  12. 1 def text_with_image opts={}
  13. 7 class_up "media-left", "m-2"
  14. 7 @image_card = Card.cardish(opts[:image]) if opts[:image]
  15. 7 haml :media_snippet, normalized_text_with_image_opts(opts)
  16. end
  17. 1 private
  18. 1 def normalized_text_with_image_opts opts
  19. 7 opts.reverse_merge! title: _render_title, text: "", size: voo.size, media_opts: {}
  20. end
  21. end
  22. end;end;end;end;
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/abstract/media.rb ~~

card/tmpsets/set/mod016-card-mod-bar_and_box/all/bar.rb

78.33% lines covered

60 relevant lines. 47 lines covered and 13 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Bar)
  4. #
  5. 1 module Bar;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/all/bar.rb"; end
  8. 1 include_set Abstract::BsBadge
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 setting :bar_cols
  11. 1 setting :info_bar_cols
  12. 1 view :info_bar do
  13. render_bar show: :bar_middle
  14. end
  15. 1 before :bar do
  16. 56 class_up "bar", card.safe_set_keys
  17. 56 voo.hide! :bar_collapse_link
  18. 56 voo.hide :edit_link, :full_page_link, :bridge_link
  19. end
  20. 1 view :bar, unknown: :unknown_bar do
  21. 56 voo.hide :bar_middle
  22. 56 voo.hide :bar_bottom # needed for toggle
  23. 56 class_up_bar_sides(voo.show?(:bar_middle))
  24. # note: above cannot be in `before`, because before blocks run before viz processing
  25. 112 wrap { haml :bar }
  26. end
  27. 1 bar_cols 9, 3
  28. 1 info_bar_cols 5, 4, 3
  29. 1 view :unknown_bar, unknown: true do
  30. voo.hide! :bar_middle, :bar_bottom, :bar_nav
  31. wrap { haml :bar }
  32. end
  33. 1 before :expanded_bar do
  34. class_up "bar", card.safe_set_keys
  35. voo.hide! :bar_expand_link
  36. end
  37. 1 view :expanded_bar do
  38. class_up_bar_sides(false)
  39. wrap { haml :expanded_bar }
  40. end
  41. 1 def class_up_bar_sides middle
  42. 56 class_up_cols %w[bar-left bar-right], bar_cols
  43. 56 class_up_cols %w[bar-left bar-middle bar-right], info_bar_cols, "md" if middle
  44. end
  45. 1 def class_up_cols classes, cols, context=nil
  46. 56 classes.each_with_index do |cls, i|
  47. 112 class_up cls, ["col", context, cols[i]].compact.join("-")
  48. end
  49. end
  50. 1 view :bar_left do
  51. 49 bar_title
  52. end
  53. 1 def bar_title
  54. 49 return render_missing if card.unknown?
  55. 49 if voo.show?(:toggle)
  56. 49 link_to_view bar_title_toggle_view, render_title
  57. else
  58. render_title
  59. end
  60. end
  61. 1 def bar_title_toggle_view
  62. 49 voo.show?(:bar_bottom) ? :bar : :expanded_bar
  63. end
  64. 1 view :bar_right, unknown: :blank do
  65. 48 [(render(:short_content) unless voo.show?(:bar_middle)),
  66. render(:edit_button, optional: :hide)]
  67. end
  68. 1 view :bar_middle, unknown: :blank do
  69. render :short_content
  70. end
  71. 1 view :bar_bottom do
  72. render(nest_mode == :edit ? :edit : :core)
  73. end
  74. 1 view :bar_nav, unknown: true, wrap: { div: { class: "bar-nav" } } do
  75. 56 [render_bar_expand_link,
  76. render_bar_collapse_link,
  77. render_full_page_link,
  78. render_edit_link,
  79. render_bridge_link]
  80. end
  81. 1 view :bar_expand_link, unknown: true do
  82. 56 link_to_view :expanded_bar, icon_tag(:keyboard_arrow_down)
  83. end
  84. 1 view :bar_collapse_link, unknown: true do
  85. link_to_view :bar, icon_tag(:keyboard_arrow_up)
  86. end
  87. 1 view :edit_button do
  88. view = voo.edit == :inline ? :edit_inline : :edit
  89. link_to_view view, "Edit", class: "btn btn-sm btn-outline-primary mr-2"
  90. end
  91. end
  92. end;end;end;end;
  93. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/all/bar.rb ~~

card/tmpsets/set/mod016-card-mod-bar_and_box/all/box.rb

66.67% lines covered

12 relevant lines. 8 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Box)
  4. #
  5. 1 module Box;
  6. 1 extend Card::Set
  7. 2 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/all/box.rb"; end
  8. 1 view :box, template: :haml do
  9. voo.hide :menu
  10. end
  11. 1 view :box_top do
  12. render_title_link
  13. end
  14. 1 view :box_middle do
  15. _render_content
  16. end
  17. 1 view :box_bottom do
  18. [_render_creator_credit,
  19. _render_updated_by]
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/all/box.rb ~~

card/tmpsets/set/mod016-card-mod-bar_and_box/self/style_media.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleMedia"
  4. #
  5. 1 module StyleMedia;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/self/style_media.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::StyleMods.add_item :style_media
  10. 1 def source_files
  11. scss_files [:image_box]
  12. end
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/self/style_media.rb ~~

card/tmpsets/set/mod016-card-mod-bar_and_box/type/image.rb

90.91% lines covered

22 relevant lines. 20 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Image" cards
  4. #
  5. 1 module Image;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/type/image.rb"; end
  8. IMAGE_BOX_SIZE_MAP = {
  9. 1 icon: :icon, small: :small, medium: :small, large: :medium, xlarge: :medium
  10. }.freeze
  11. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  12. 1 view :boxed, unknown: true do
  13. 14 image_box { |size| render_core size: size }
  14. end
  15. 1 view :boxed_link, unknown: true do
  16. image_box { |size| link_to_card image_box_link_target, render_core(size: size) }
  17. end
  18. 1 def image_box
  19. 7 voo.size ||= :medium
  20. 7 wrap_with :div, title: image_box_title, class: "image-box #{voo.size}" do
  21. 7 yield image_box_size
  22. end
  23. end
  24. ## METHODS FOR OVERRIDE
  25. 1 def image_box_size
  26. 7 IMAGE_BOX_SIZE_MAP[voo.size.to_sym] || :medium
  27. end
  28. 1 def image_box_card_name
  29. 7 card.name.junction? ? card.name.left : card.name
  30. end
  31. 1 def image_box_link_target
  32. image_box_card_name
  33. end
  34. 1 def image_box_title
  35. 7 voo.title || image_box_card_name
  36. end
  37. end
  38. end;end;end;end;
  39. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bar_and_box/set/type/image.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/abstract/bootstrap_code_file.rb

54.29% lines covered

35 relevant lines. 19 lines covered and 16 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (BootstrapCodeFile)
  4. #
  5. 1 module BootstrapCodeFile;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootstrap_code_file.rb"; end
  8. 1 def self.included host_class
  9. 3 host_class.include_set Abstract::CodeFile
  10. 3 host_class.include OverrideCodeFile
  11. end
  12. 1 module OverrideCodeFile
  13. 1 def content
  14. stylesheets.join "\n"
  15. end
  16. 1 def stylesheets
  17. load_stylesheets unless @stylesheets
  18. @stylesheets
  19. end
  20. 1 def add_bs_subdir sub_dir
  21. Dir.glob("#{bootstrap_path}/#{sub_dir}/*.scss").each do |path|
  22. load_from_path path
  23. end
  24. end
  25. 1 def mod_path
  26. mod_root :bootstrap
  27. end
  28. 1 def bootstrap_path
  29. "#{mod_path}/vendor/bootstrap/scss"
  30. end
  31. 1 def add_stylesheet filename, type: :scss
  32. load_from_path "#{mod_path}/lib/stylesheets/#{filename}.#{type}"
  33. end
  34. 1 def add_stylesheet_file path
  35. load_from_path File.join(mod_path, path)
  36. end
  37. 1 def add_bs_stylesheet filename, type: :scss, subdir: nil
  38. path = File.join(*[bootstrap_path, subdir, "_#{filename}.#{type}"].compact)
  39. load_from_path path
  40. end
  41. 1 def load_from_path path
  42. @stylesheets ||= []
  43. Rails.logger.info "reading file: #{path}"
  44. @stylesheets << File.read(path)
  45. end
  46. 1 def source_changed _since:
  47. false
  48. end
  49. 1 def existing_source_paths
  50. []
  51. end
  52. end
  53. end;end;end;end;
  54. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootstrap_code_file.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/abstract/bootswatch_theme.rb

53.57% lines covered

56 relevant lines. 30 lines covered and 26 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (BootswatchTheme)
  4. #
  5. # Bootswatch themes are free themes for bootstrap available at https://bootswatch.com/.
  6. # For every bootswatch theme we have one card of card type "bootswatch skin".
  7. # They all have codenames following the pattern "#{theme_name}_skin".
  8. #
  9. # The original bootswatch theme is build from two files, `_variables.scss` and
  10. # `_bootswatch.scss`. The original bootstrap scss has to be put between those two.
  11. # `_variables.scss` overrides bootstrap variables, `_bootswatch.scss` overrides
  12. # bootstrap css (variables are defined with `!default` hence only the first appearance
  13. # has an effect, for css the last appearance counts)
  14. #
  15. # The content of a bootswatch theme card consists of four parts:
  16. # * pre_variables: hard-coded theme independent stuff
  17. # and bootstrap functions to make them available in the variables part
  18. # * variables: the content from `_variables.scss`,
  19. # * post_variables: the bootstrap css and libraries like select2 and
  20. # bootstrap-colorpicker that depend on the theme
  21. # * stylesheets: the content from `_bootswatch.scss` and custom styles
  22. #
  23. # For the original bootswatch themes all those parts are hard-coded and the content
  24. 1 module BootswatchTheme;
  25. 1 extend Card::Set
  26. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootswatch_theme.rb"; end
  27. # is taken from files.
  28. # The bootswatch theme content is taken directly from the files in the bootswatch
  29. # submodule. For the rest we use code file cards.
  30. # Cards of type "customized bootswatch skin" have the same structure but make the variables
  31. # and stylesheets part editable.
  32. #
  33. # Bootswatch theme cards are machine cards for the following reason.
  34. # Machine cards usually store all involved input cards of all nested levels in
  35. # there +*machine_input pointer. All those input cards
  36. # are processed separately and the result is joined to build the machine output.
  37. # That's a problem for this card when it's used as input.
  38. # A lot of the items depend on the variables scss and can't
  39. # be processed independently. Therefore we return only self as item card and join
  40. # the content of all the item cards in the content of the bootswatch theme card.
  41. # But then this card has to forward updates of its items to the machine cards it provides
  42. # input for.
  43. # To do that it is a machine itself and stores the generated machine output as its
  44. # content which will trigger the updates of other machines that use this card.
  45. 1 include_set Abstract::Machine
  46. 1 include_set Type::Scss
  47. 1 include_set Abstract::CodeFile
  48. 1 include_set Abstract::SkinBox
  49. 1 CONTENT_PARTS = %i[pre_variables variables post_variables stylesheets].freeze
  50. 1 PRE_VARIABLES_CARD_NAMES = %i[
  51. style_jquery_ui_smoothness
  52. bootstrap_functions
  53. ].freeze
  54. 1 POST_VARIABLES_CARD_NAMES = %i[
  55. bootstrap_core
  56. style_cards
  57. style_bootstrap_cards
  58. style_libraries
  59. style_mods
  60. ].freeze
  61. # @return [Array<Card::Name,String>]
  62. 1 def variables_card_names
  63. []
  64. end
  65. # @return [Array<Card::Name,String>]
  66. 1 def stylesheets_card_names
  67. []
  68. end
  69. # reject cards that don't contribute directly to the content like skin or pointer cards
  70. 1 def engine_input
  71. extended_input_cards.select { |ca| ca.type_id.in? [Card::ScssID, Card::CssID] }
  72. end
  73. # Don't create "+*machine output" file card
  74. # instead save the the output as the card's content is
  75. 1 def after_engine output
  76. Card::Auth.as_bot { update! db_content: output }
  77. end
  78. # needed to make the refresh_script_and_style method work with these cards
  79. 1 def source_files
  80. extended_input_cards.map do |i_card|
  81. i_card.try(:source_files)
  82. end.flatten.compact
  83. end
  84. # needed to make the refresh_script_and_style method work with these cards
  85. 1 def existing_source_paths
  86. extended_input_cards.map do |i_card|
  87. i_card.try(:existing_source_paths)
  88. end.flatten.compact
  89. end
  90. 1 def extended_input_cards
  91. input_names.map do |n|
  92. Card.fetch(n).extended_item_cards
  93. end.flatten.compact
  94. end
  95. 1 def content
  96. CONTENT_PARTS.map do |n|
  97. send "#{n}_content"
  98. end.join "\n"
  99. end
  100. 1 def pre_variables_content
  101. load_content(*PRE_VARIABLES_CARD_NAMES)
  102. end
  103. 1 def variables_content
  104. load_content variables_card_names
  105. end
  106. 1 def post_variables_content
  107. load_content(*POST_VARIABLES_CARD_NAMES)
  108. end
  109. 1 def stylesheets_content
  110. load_content stylesheets_card_names
  111. end
  112. 1 def input_names _args={}
  113. (PRE_VARIABLES_CARD_NAMES + variables_card_names +
  114. POST_VARIABLES_CARD_NAMES + stylesheets_card_names).compact.map do |n|
  115. Card.fetch_name(n)
  116. end.compact
  117. end
  118. 1 def item_names _args={}
  119. []
  120. end
  121. 1 def item_cards _args={}
  122. [self]
  123. end
  124. 1 def load_content *names
  125. cards = names.flatten.map { |n| Card.fetch(n)&.extended_item_cards }
  126. cards.flatten.compact.map(&:content).join "\n"
  127. end
  128. 1 def scss_from_theme_file file
  129. return "" unless (path = ::File.join(source_dir, "_#{file}.scss")) &&
  130. ::File.exist?(path)
  131. ::File.read path
  132. end
  133. 1 def theme_name
  134. /^(.+)_skin$/.match(codename)&.capture(0) ||
  135. /^(.+)[ _][sS]kin/.match(name)&.capture(0)&.downcase
  136. end
  137. 1 def source_dir
  138. @source_dir ||= File.expand_path(
  139. "#{mod_root :bootstrap}/vendor/bootswatch/dist/#{theme_name}", __FILE__
  140. )
  141. end
  142. end;end;end;end;
  143. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootswatch_theme.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/abstract/bootswatch_theme/html_views.rb

80.0% lines covered

20 relevant lines. 16 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module BootswatchTheme;
  3. # Set: Abstract (BootswatchTheme, HtmlViews)
  4. #
  5. 1 module HtmlViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootswatch_theme/html_views.rb"; end
  8. 1 include_set Abstract::Media
  9. 1 include_set Abstract::BsBadge
  10. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  11. 1 before :box do
  12. voo.show! :customize_button, :box_middle
  13. end
  14. 1 view :one_line_content do
  15. ""
  16. end
  17. 1 view :bar_left do
  18. 7 class_up "card-title", "my-0 ml-2"
  19. 7 class_up "media-left", "m-0"
  20. 7 text_with_image size: :medium, title: "", text: _render_title,
  21. media_opts: { class: "align-items-center" }
  22. # field_nest(:image, view: :core) + wrap_with(:h4, render(:title))
  23. end
  24. 1 view :bar_right do
  25. 7 customize_button text: "Customize"
  26. end
  27. 1 view :bar_bottom do
  28. wrap_with :code do
  29. render_core
  30. end
  31. end
  32. end
  33. end;end;end;end;end;
  34. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/abstract/bootswatch_theme/html_views.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/accordion.rb

28.57% lines covered

28 relevant lines. 8 lines covered and 20 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bootstrap;
  3. # Set: All cards (Bootstrap, Accordion)
  4. #
  5. 1 module Accordion;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/accordion.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def accordion_group list, collapse_id=nil, args={}
  10. collapse_id ||= card.name.safe_key
  11. accordions = ""
  12. index = 1
  13. case list
  14. when Array then accordions = list.join
  15. when String then accordions = list
  16. else
  17. list.each_pair do |title, content|
  18. accordions << accordion(title, content, "#{collapse_id}-#{index}")
  19. index += 1
  20. end
  21. end
  22. add_class args, "act-accordion-group w-100"
  23. wrap_with :div, class: args[:class], id: "accordion-#{collapse_id}",
  24. role: "tablist", "aria-multiselectable" => "true" do
  25. accordions
  26. end
  27. end
  28. 1 def accordion title, content, collapse_id=card.name.safe_key
  29. accordion_content =
  30. case content
  31. when Hash then accordion_group content, collapse_id
  32. when Array then content.present? && list_group(content)
  33. when String then content
  34. end
  35. <<-HTML.html_safe
  36. <div class="card">
  37. #{accordion_panel(title, accordion_content, collapse_id)}
  38. </div>
  39. HTML
  40. end
  41. 1 def accordion_panel title, body, collapse_id, _panel_heading_link=false
  42. if body
  43. <<-HTML
  44. <div class="card-header" role="tab" id="heading-#{collapse_id}">
  45. <h5 class="mb-0">
  46. <a data-toggle="collapse" data-parent="#accordion-#{collapse_id}" \
  47. href="##{collapse_id}" aria-expanded="true" \
  48. aria-controls="#{collapse_id}">
  49. #{title}
  50. </a>
  51. </h5>
  52. </div>
  53. <div id="#{collapse_id}" class="collapse" \
  54. role="tabpanel" aria-labelledby="heading-#{collapse_id}">
  55. <div class="card-body">
  56. #{body}
  57. </div>
  58. </div>
  59. HTML
  60. else
  61. <<-HTML
  62. <li class="list-group-item">
  63. <h4 class="card-header">#{title}</h4>
  64. </li>
  65. HTML
  66. end
  67. end
  68. end
  69. end;end;end;end;end;
  70. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/accordion.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/dropdown.rb

82.35% lines covered

34 relevant lines. 28 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bootstrap;
  3. # Set: All cards (Bootstrap, Dropdown)
  4. #
  5. 1 module Dropdown;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/dropdown.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def dropdown_button name, items_or_opts={}, opts={}
  10. items = block_given? ? yield : items_or_opts
  11. opts = items_or_opts if block_given?
  12. <<-HTML
  13. <div class="btn-group #{opts[:extra_css_class]}" role="group">
  14. <button class="btn btn-primary dropdown-toggle"
  15. data-toggle="dropdown" title="#{name}" aria-expanded="false"
  16. aria-haspopup="true">
  17. #{icon_tag opts[:icon] if opts[:icon]} #{name}
  18. <span class="caret"></span>
  19. </button>
  20. #{dropdown_list items, opts[:class], opts[:active]}
  21. </div>
  22. HTML
  23. end
  24. 1 def split_button main_button, active_item
  25. 202 wrap_with :div, class: "btn-group" do
  26. [
  27. 202 main_button,
  28. split_button_toggle,
  29. dropdown_list(yield, "dropdown-menu-right", active_item)
  30. ]
  31. end
  32. end
  33. 1 private
  34. # @param items
  35. # [String] plain html
  36. # [Array<String, Array>] list of item names
  37. # If an item is an array then the first element is used as header for a section.
  38. # The second item has to be an array with the item names for that section.
  39. # [Hash] key is used to identify active item, value is the item name.
  40. # @param active specifies which item to highlight as active. If items are given as array
  41. # it has to be the index of the active item. If items are given as hash it has to be
  42. # the key of that item.
  43. 1 def dropdown_list items, extra_css_class=nil, active=nil
  44. 202 wrap_with :ul, class: "dropdown-menu #{extra_css_class}", role: "menu" do
  45. list =
  46. 202 case items
  47. when Array
  48. 202 dropdown_array_list items, active
  49. when Hash
  50. dropdown_hash_list items, active
  51. else
  52. [items]
  53. end
  54. 202 list.flatten.compact.join "\n"
  55. end
  56. end
  57. 1 def dropdown_header text
  58. 190 content_tag(:h6, text, class: "dropdown-header")
  59. end
  60. 1 def dropdown_hash_list items, active=nil
  61. items.map { |key, item| dropdown_list_item item, key, active }
  62. end
  63. 1 def dropdown_array_list items, active=nil
  64. 1422 items.map.with_index { |item, i| dropdown_list_item item, i, active }
  65. end
  66. 1 def dropdown_list_item item, active_test, active
  67. 1030 return unless item
  68. 1018 if item.is_a? Array
  69. 190 [dropdown_header(item.first), dropdown_array_list(item.second)]
  70. else
  71. 828 "<li class='dropdown-item#{' active' if active_test == active}'>#{item}</li>"
  72. end
  73. end
  74. 1 def split_button_toggle
  75. 202 wrap_with(:a,
  76. href: "#",
  77. class: "nav-link pl-0 dropdown-toggle dropdown-toggle-split",
  78. "data-toggle" => "dropdown",
  79. "aria-haspopup" => "true",
  80. "aria-expanded" => "false") do
  81. 202 '<span class="sr-only">Toggle Dropdown</span>'
  82. end
  83. end
  84. end
  85. end;end;end;end;end;
  86. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/dropdown.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/form.rb

100.0% lines covered

23 relevant lines. 23 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bootstrap;
  3. # Set: All cards (Bootstrap, Form)
  4. #
  5. 1 module Form;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/form.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def button_tag content_or_options=nil, options={}, &block
  10. 172 bootstrapify_button(block_given? ? content_or_options : options)
  11. 172 super(content_or_options, options, &block)
  12. end
  13. 1 def bootstrapify_button options
  14. 172 situation = options.delete(:situation) || "primary"
  15. 172 options[:class] = [options[:class], "btn", "btn-#{situation}"].compact * " "
  16. end
  17. 1 def type_field args={}
  18. 4 args[:class] ||= ""
  19. 4 args[:class] += " form-control"
  20. 4 super(args)
  21. end
  22. 1 def bootstrap_options options
  23. 483 options[:class] ||= ""
  24. 483 options[:class] += " form-control"
  25. 483 options
  26. end
  27. 1 FIELD_HELPERS = %w[hidden_field color_field date_field datetime_field
  28. datetime_local_field email_field month_field number_field
  29. password_field phone_field range_field search_field
  30. telephone_field text_area text_field time_field
  31. url_field week_field file_field].freeze
  32. 1 FIELD_HELPERS.each do |method_name|
  33. 19 define_method(method_name) do |name, options={}|
  34. 483 form.send(method_name, name, bootstrap_options(options))
  35. end
  36. end
  37. end
  38. end;end;end;end;end;
  39. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/form.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/helper.rb

40.0% lines covered

40 relevant lines. 16 lines covered and 24 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bootstrap;
  3. # Set: All cards (Bootstrap, Helper)
  4. #
  5. 1 module Helper;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/helper.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def button_link link_text, opts={}
  10. btn_type = opts.delete(:btn_type) || "primary"
  11. opts[:class] = [opts[:class], "btn btn-#{btn_type}"].compact.join " "
  12. smart_link_to link_text, opts
  13. end
  14. 1 def separator
  15. '<li role="separator" class="divider"></li>'
  16. end
  17. 1 def list_group content_or_options=nil, options={}
  18. options = content_or_options if block_given?
  19. content = block_given? ? yield : content_or_options
  20. content = Array(content).map(&:to_s)
  21. add_class options, "list-group"
  22. options[:items] ||= {}
  23. add_class options[:items], "list-group-item"
  24. list_tag content, options
  25. end
  26. 1 def list_tag content_or_options=nil, options={}
  27. options = content_or_options if block_given?
  28. content = block_given? ? yield : content_or_options
  29. content = Array(content)
  30. default_item_options = options.delete(:items) || {}
  31. tag = options[:ordered] ? :ol : :ul
  32. wrap_with tag, options do
  33. content.map do |item|
  34. i_content, i_opts = item
  35. i_opts ||= default_item_options
  36. wrap_with :li, i_content, i_opts
  37. end
  38. end
  39. end
  40. 1 def badge_tag content, options={}
  41. add_class options, "badge"
  42. wrap_with :span, content, options
  43. end
  44. 1 def popover_link text, title=nil, link_text=fa_icon("question-circle"), opts={}
  45. link_to link_text, popover_opts(text, title, opts)
  46. end
  47. 1 def popover_opts text, title, opts
  48. 255 add_class opts, "pl-1 text-muted-link"
  49. 255 opts.merge! path: "#", "data-toggle": "popover",
  50. "data-trigger": :focus, "data-content": text
  51. 255 opts["data-title"] = title if title
  52. 255 opts
  53. end
  54. end
  55. end;end;end;end;end;
  56. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/helper.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/icon.rb

91.18% lines covered

34 relevant lines. 31 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bootstrap;
  3. # Set: All cards (Bootstrap, Icon)
  4. #
  5. 1 module Icon;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/icon.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # same for all:
  10. # :search,
  11. ICON_MAP = {
  12. 1 material: {
  13. plus: :add,
  14. pencil: :edit,
  15. trash: :delete,
  16. wrench: :build,
  17. new_window: :open_in_new,
  18. history: :history,
  19. triangle_left: :expand_less,
  20. triangle_right: :expand_more,
  21. flag: :flag,
  22. option_horizontal: :more_horiz,
  23. option_vertical: :more_vert,
  24. pushpin: :pin_drop,
  25. baby_formula: :device_hub,
  26. log_out: :call_made,
  27. log_in: :call_received,
  28. explore: :explore,
  29. remove: :close,
  30. expand: :expand_more,
  31. collapse_down: :expand_less,
  32. globe: :public,
  33. check_circle_o: nil,
  34. commenting: :comment
  35. },
  36. font_awesome: {
  37. option_horizontal: :ellipsis_h,
  38. pushpin: "thumb-tack",
  39. globe: :globe,
  40. zoom_out: "search-minus",
  41. close: :remove,
  42. check_circle_o: "check-circle-o",
  43. check_circe: "check-circle",
  44. reorder: "align-justify",
  45. commenting: :commenting
  46. },
  47. glyphicon: {
  48. option_horizontal: "option-horizontal",
  49. option_vertical: "option-vertical",
  50. triangle_left: "triangle-left",
  51. triangle_right: "triangle-right",
  52. baby_formula: "baby-formula",
  53. log_out: "log-out",
  54. log_in: "log-in",
  55. collapse_down: "collapse-down",
  56. globe: :globe,
  57. zoom_out: "zoom-out",
  58. close: :remove,
  59. new_window: "new-window",
  60. history: :time,
  61. check_circle_o: "ok-circle",
  62. check_circle: "ok-sign",
  63. reorder: "align-justify"
  64. }
  65. }.freeze
  66. 1 def icon_class library, icon
  67. 1054 ICON_MAP[library][icon] || icon
  68. end
  69. 1 def material_icon icon, opts={}
  70. 817 universal_icon_tag icon, :material, opts
  71. end
  72. 1 def glyphicon icon, opts={}
  73. universal_icon_tag icon, :glyphicon, opts
  74. end
  75. 1 def fa_icon icon, opts={}
  76. 25 universal_icon_tag icon, :font_awesome, opts
  77. end
  78. 1 def icon_tag icon, opts={}
  79. 212 opts = { class: opts } unless opts.is_a? Hash
  80. 212 library = opts.delete(:library) || default_icon_library
  81. 212 universal_icon_tag icon, library, opts
  82. end
  83. 1 def universal_icon_tag icon, icon_library=default_icon_library, opts={}
  84. 1054 return "" unless icon.present?
  85. 1054 opts = { class: opts } unless opts.is_a? Hash
  86. 1054 icon_method = "#{icon_library}_icon_tag"
  87. 1054 send icon_method, icon, opts
  88. end
  89. 1 def default_icon_library
  90. 212 :material
  91. end
  92. 1 def glyphicon_icon_tag icon, opts={}
  93. prepend_class opts, "glyphicon glyphicon-#{icon_class(:glyphicon, icon)}"
  94. wrap_with :span, "", opts.merge("aria-hidden": true)
  95. end
  96. 1 def font_awesome_icon_tag icon, opts={}
  97. 25 prepend_class opts, "fa fa-#{icon_class(:font_awesome, icon)}"
  98. 25 wrap_with :i, "", opts
  99. end
  100. 1 def material_icon_tag icon, opts={}
  101. 1029 add_class opts, "material-icons"
  102. 1029 wrap_with :i, icon_class(:material, icon), opts
  103. end
  104. end
  105. end;end;end;end;end;
  106. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/icon.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/navbar.rb

33.33% lines covered

33 relevant lines. 11 lines covered and 22 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bootstrap;
  3. # Set: All cards (Bootstrap, Navbar)
  4. #
  5. 1 module Navbar;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/navbar.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # Options
  10. # @param opts [Hash]
  11. # @option opts [String, Hash<name, href>] brand
  12. # @option opts [String] class
  13. # @option opts [Boolean] no_collapse
  14. # @option opts [:left, :right] toggle_align
  15. 1 def navbar id, opts={}
  16. nav_opts = opts[:navbar_opts] || {}
  17. nav_opts[:class] ||= opts[:class]
  18. add_class nav_opts,
  19. "navbar navbar-dark bg-#{opts.delete(:navbar_type) || 'primary'}"
  20. content = yield
  21. if opts[:no_collapse]
  22. navbar_nocollapse content, nav_opts
  23. else
  24. navbar_responsive id, content, opts, nav_opts
  25. end
  26. end
  27. 1 def navbar_nocollapse content, nav_opts
  28. # content = wrap_with(:div, content)
  29. wrap_with :nav, content, nav_opts
  30. end
  31. 1 def navbar_responsive id, content, opts, nav_opts
  32. opts[:toggle_align] ||= :right
  33. wrap_with :nav, nav_opts do
  34. [
  35. navbar_header(opts[:brand]),
  36. navbar_toggle(id, opts[:toggle_align]),
  37. wrap_with(:div, class: "collapse navbar-collapse",
  38. id: "navbar-collapse-#{id}") { content }
  39. ]
  40. end
  41. end
  42. 1 def navbar_header brand
  43. return "" unless brand
  44. if brand.is_a? String
  45. "<span class='navbar-brand'>#{brand}</span>"
  46. else
  47. link = brand[:href] || "#"
  48. "<a class='navbar-brand' href='#{link}#'>#{brand[:name]}</a>"
  49. end
  50. end
  51. 1 def navbar_toggle id, align
  52. content ||= %(<span class="navbar-toggler-icon"></span>)
  53. <<-HTML
  54. <button class="navbar-toggler navbar-toggler-#{align}" type="button" data-toggle="collapse" data-target="#navbar-collapse-#{id}" aria-controls="navbar-collapse-#{id}" aria-expanded="false" aria-label="Toggle navigation">
  55. #{content}
  56. </button>
  57. HTML
  58. end
  59. 1 def breadcrumb items
  60. wrap_with :ol, class: "breadcrumb" do
  61. items.map do |item|
  62. wrap_with :li, item, class: "breadcrumb-item"
  63. end.join
  64. end
  65. end
  66. end
  67. end;end;end;end;end;
  68. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/navbar.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/table.rb

29.09% lines covered

55 relevant lines. 16 lines covered and 39 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bootstrap;
  3. # Set: All cards (Bootstrap, Table)
  4. #
  5. 1 module Table;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/table.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 class TableHelper
  10. 1 def initialize format, content, opts={}
  11. @format = format
  12. @div_table = opts.delete :div_table
  13. @header = initialize_header opts[:header], content
  14. @rows = content
  15. @opts = opts
  16. @format.add_class opts, :table
  17. end
  18. 1 def render
  19. tag :table, class: @opts[:class] do
  20. [header, body]
  21. end
  22. end
  23. 1 def header
  24. return unless @header
  25. tag :thead do
  26. tag :tr do
  27. @header.map do |item|
  28. tag(:th) { item }
  29. end.join "\n"
  30. end
  31. end
  32. end
  33. 1 def body
  34. tag :tbody do
  35. @rows.map do |row_content|
  36. row row_content
  37. end.join "\n"
  38. end
  39. end
  40. 1 def row row
  41. row_data, row_class =
  42. case row
  43. when Hash then
  44. [row.delete(:content), row]
  45. else
  46. [row, {}]
  47. end
  48. row_content =
  49. if row_data.is_a?(Array)
  50. row_data.map { |item| cell item }.join "\n"
  51. else
  52. row_data
  53. end
  54. tag :tr, row_content, row_class
  55. end
  56. 1 def cell cell
  57. if cell.is_a? Hash
  58. content = cell.delete(:content).to_s
  59. tag :td, cell do
  60. content
  61. end
  62. else
  63. tag :td do
  64. String(cell)
  65. end
  66. end
  67. end
  68. 1 def tag elem, content_or_opts={}, opts={}, &block
  69. if @div_table
  70. if content_or_opts.is_a? Hash
  71. @format.add_class content_or_opts, elem
  72. else
  73. @format.add_class opts, elem
  74. end
  75. elem = :div
  76. end
  77. @format.wrap_with elem, content_or_opts, opts, &block
  78. end
  79. 1 private
  80. 1 def initialize_header header, content
  81. case header
  82. when Array then header
  83. when nil then nil
  84. else content.shift
  85. end
  86. end
  87. end
  88. # @param [Array<Array,String>] content the content for the table. Accepts
  89. # strings or arrays for each row.
  90. # @param [Hash] opts
  91. # @option opts [String, Array] :header use first row of content as header or
  92. # value of this option if it is a string
  93. # @return [HTML] bootstrap table
  94. 1 def table content, opts={}
  95. TableHelper.new(self, content, opts).render
  96. end
  97. end
  98. end;end;end;end;end;
  99. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/table.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/tabs.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bootstrap;
  3. # Set: All cards (Bootstrap, Tabs)
  4. #
  5. 1 module Tabs;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/tabs.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # @param tab_hash [Hash] keys are the tab names
  10. # Each value can be either a String or a Hash.
  11. # If a Hash can contain the following keys:
  12. # :title - the label to appear in the clickable tab nav.
  13. # if title is not specified, the key is used
  14. # :content - body of tab pane
  15. # :button_attr - attributes for button link in tab nav.
  16. #
  17. # If using lazy loading (see :load below), the following options also apply
  18. # :path - explicit path to use for tab pane
  19. # :view - card view from which to auto-construct path (if missing, uses key)
  20. #
  21. # If the value is a String, it is treated as the tab content for static tabs and
  22. # the view for lazy tabs
  23. #
  24. # @param active_name [String] label of the tab that should be active at the
  25. #
  26. # @param [Hash] args options
  27. # @option args [String] :tab_type ('tabs') use pills or tabs
  28. # @option args [Hash] :panel_attr html args used for the panel div
  29. # @option args [Hash] :pane_attr html args used for the pane div
  30. # @option args [Hash] :load. `:lazy` for lazy-loading tabs
  31. #
  32. # @param [Block] block content of the active tab (for lazy-loading)
  33. # beginning (default is the first)
  34. #
  35. # @return [HTML] bootstrap tabs element with all content preloaded
  36. 1 def tabs tab_hash, active_name=nil, args={}, &block
  37. 2 klass = args[:load] == :lazy ? Card::LazyTab : Card::Tab
  38. 2 args.reverse_merge!(
  39. panel_attr: {},
  40. pane_attr: {},
  41. tab_type: "tabs",
  42. block: block,
  43. tab_objects: Card::Tab.tab_objects(self, tab_hash, active_name, klass)
  44. )
  45. 2 haml :tab_panel, args
  46. end
  47. end
  48. end;end;end;end;end;
  49. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/tabs.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/bootstrap/wrapper.rb

93.33% lines covered

15 relevant lines. 14 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Bootstrap;
  3. # Set: All cards (Bootstrap, Wrapper)
  4. #
  5. 1 module Wrapper;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/wrapper.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def frame
  10. 52 class_up "d0-card-header" , "card-header"
  11. 52 class_up "d0-card-body", "card-body card-text"
  12. 52 super
  13. end
  14. 1 def standard_frame slot=true
  15. 52 if panel_state
  16. class_up "d0-card-frame", "card bg-#{panel_state} text-white"
  17. else
  18. 52 class_up "d0-card-frame", "card"
  19. end
  20. 52 super
  21. end
  22. 1 def panel_state
  23. end
  24. end
  25. end;end;end;end;end;
  26. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/bootstrap/wrapper.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/all/rich_bootstrap.rb

66.67% lines covered

12 relevant lines. 8 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (RichBootstrap)
  4. #
  5. 1 module RichBootstrap;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/rich_bootstrap.rb"; end
  8. 1 def read_bootstrap_variables
  9. path = ::File.expand_path(
  10. "#{mod_root :bootstrap}/vendor/bootstrap/scss/_variables.scss", __FILE__
  11. )
  12. ::File.exist?(path) ? ::File.read(path) : ""
  13. end
  14. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  15. 1 view :closed do
  16. class_up "d0-card-body", "closed-content"
  17. super()
  18. end
  19. 1 include Bootstrapper
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/all/rich_bootstrap.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/bootstrap_core.rb

54.55% lines covered

11 relevant lines. 6 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "BootstrapCore"
  4. #
  5. 1 module BootstrapCore;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/bootstrap_core.rb"; end
  8. 1 include_set Abstract::BootstrapCodeFile
  9. 1 def load_stylesheets
  10. add_bs_stylesheet "variables"
  11. add_bs_subdir "mixins"
  12. %w[ print reboot type images code grid tables forms buttons transitions dropdown
  13. button-group input-group custom-forms nav navbar card breadcrumb pagination badge
  14. jumbotron alert progress media list-group close modal tooltip popover carousel
  15. ].each do |name|
  16. add_bs_stylesheet name
  17. end
  18. add_bs_subdir "utilities"
  19. end
  20. end;end;end;end;
  21. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/bootstrap_core.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/bootstrap_functions.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "BootstrapFunctions"
  4. #
  5. 1 module BootstrapFunctions;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/bootstrap_functions.rb"; end
  8. 1 include_set Abstract::BootstrapCodeFile
  9. 1 def load_stylesheets
  10. add_bs_stylesheet "functions"
  11. end
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/bootstrap_functions.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/font_awesome.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "FontAwesome"
  4. #
  5. 1 module FontAwesome;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/font_awesome.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::StyleLibraries.add_item :font_awesome
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/font_awesome.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/material_icons.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "MaterialIcons"
  4. #
  5. 1 module MaterialIcons;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/material_icons.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::StyleLibraries.add_item :material_icons
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/material_icons.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/script_bootstrap.rb

80.0% lines covered

10 relevant lines. 8 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptBootstrap"
  4. #
  5. 1 module ScriptBootstrap;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_bootstrap.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptLibraries.add_item :script_bootstrap
  10. 1 def source_dir
  11. ""
  12. end
  13. 1 def source_files
  14. %w[vendor/bootstrap/dist/js/bootstrap.bundle.js
  15. lib/javascript/bootstrap_modal_decko.js
  16. vendor/bootstrap-colorpicker/dist/js/bootstrap-colorpicker.min.js]
  17. end
  18. end;end;end;end;
  19. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_bootstrap.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/script_load_select2.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptLoadSelect2"
  4. #
  5. 1 module ScriptLoadSelect2;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_load_select2.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptMods.add_item :script_load_select2
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_load_select2.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/script_select2.rb

80.0% lines covered

10 relevant lines. 8 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptSelect2"
  4. #
  5. 1 module ScriptSelect2;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_select2.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptLibraries.add_item :script_select2
  10. 1 def source_files
  11. # full version of select2 is needed to support containerCssClass config option
  12. # which we need for select2_bootstrap to work properly
  13. "vendor/select2/dist/js/select2.full.min.js"
  14. end
  15. 1 def source_dir
  16. ""
  17. end
  18. end;end;end;end;
  19. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/script_select2.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/smartmenu_css.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "SmartmenuCss"
  4. #
  5. 1 module SmartmenuCss;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/smartmenu_css.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 def source_files
  10. "startmenu.css"
  11. end
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/smartmenu_css.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/smartmenu_js.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "SmartmenuJs"
  4. #
  5. 1 module SmartmenuJs;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/smartmenu_js.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 def source_files
  10. "startmenu.js"
  11. end
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/smartmenu_js.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/style_bootstrap_cards.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleBootstrapCards"
  4. #
  5. 1 module StyleBootstrapCards;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_bootstrap_cards.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_bootstrap_cards.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/style_bootstrap_colorpicker.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleBootstrapColorpicker"
  4. #
  5. 1 module StyleBootstrapColorpicker;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_bootstrap_colorpicker.rb"; end
  8. 1 include_set Abstract::BootstrapCodeFile
  9. 1 Self::StyleLibraries.add_item :style_bootstrap_colorpicker
  10. 1 def load_stylesheets
  11. add_stylesheet_file "vendor/bootstrap-colorpicker/src/sass/_colorpicker.scss"
  12. end
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_bootstrap_colorpicker.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/style_select2.rb

80.0% lines covered

10 relevant lines. 8 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleSelect2"
  4. #
  5. 1 module StyleSelect2;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_select2.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::StyleLibraries.add_item :style_select2
  10. 1 def source_files
  11. ["vendor/select2/dist/css/select2.css", "lib/stylesheets/style_select2_bootstrap.scss"]
  12. end
  13. 1 def source_dir
  14. ""
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_select2.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/self/style_select2_bootstrap.rb

100.0% lines covered

5 relevant lines. 5 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleSelect2Bootstrap"
  4. #
  5. 1 module StyleSelect2Bootstrap;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_select2_bootstrap.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. end;end;end;end;
  10. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/self/style_select2_bootstrap.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/type/bootswatch_skin.rb

77.78% lines covered

9 relevant lines. 7 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "BootswatchSkin" cards
  4. #
  5. 1 module BootswatchSkin;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/bootswatch_skin.rb"; end
  8. 1 include_set Abstract::BootswatchTheme
  9. # override to customize the theme or to make it customizable
  10. # @return [Card, String, Array<Card, String>] strings must be valid (s)css; cards
  11. # must be of type (S)CSS
  12. 1 def variables_content
  13. scss_from_theme_file :variables
  14. end
  15. # override to customize the theme or to make it customizable
  16. # @return [Card, String, Array<Card,String>] strings must be valid (s)css; cards
  17. # must be of type (S)CSS
  18. 1 def stylesheets_content
  19. scss_from_theme_file :bootswatch
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/bootswatch_skin.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/type/customized_bootswatch_skin.rb

41.18% lines covered

68 relevant lines. 28 lines covered and 40 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "CustomizedBootswatchSkin" cards
  4. #
  5. 1 module CustomizedBootswatchSkin;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/customized_bootswatch_skin.rb"; end
  8. 1 include_set Abstract::BootswatchTheme
  9. 1 card_accessor :colors
  10. 1 card_accessor :variables
  11. 1 card_accessor :stylesheets
  12. 1 def top_level_item_cards
  13. cards = PRE_VARIABLES_CARD_NAMES.map { |n| Card[n] }
  14. cards += [colors_card, variables_card]
  15. cards += POST_VARIABLES_CARD_NAMES.map { |n| Card[n] }
  16. cards << stylesheets_card
  17. cards
  18. end
  19. 1 def editable_item_cards
  20. [colors_card, variables_card, stylesheets_card]
  21. end
  22. 1 def variables_card_names
  23. %i[colors variables].map { |s| Card.fetch_name name, s }
  24. end
  25. 1 def stylesheets_card_names
  26. [Card.fetch_name(name, :stylesheets)]
  27. end
  28. 1 def theme_card_name
  29. "#{theme_name} skin"
  30. end
  31. 1 def theme_name
  32. Env.params[:theme].present? && Env.params[:theme]
  33. end
  34. 1 def theme_codename
  35. theme_name && "#{theme_name}_skin".to_sym
  36. end
  37. 1 event :validate_theme_template, :validate, on: :create do
  38. if theme_name
  39. if Card.fetch_type_id(theme_card_name) != Card::BootswatchSkinID
  40. errors.add :abort, tr(:not_valid_theme, theme_name: theme_name)
  41. elsif !Dir.exist? source_dir
  42. puts method(:source_dir).source_location
  43. errors.add :abort, tr(:cannot_source_theme, theme_name: theme_name)
  44. end
  45. end
  46. end
  47. 1 event :initialize_because_of_type_change, :prepare_to_store,
  48. on: :update, changed: :type do
  49. initialize_theme old_skin_items
  50. end
  51. 1 def old_skin_items
  52. skin = Card.new(type: :pointer, content: db_content_before_act)
  53. skin.drop_item "bootstrap default skin"
  54. skin.item_names
  55. end
  56. 1 event :copy_theme, :prepare_to_store, on: :create do
  57. initialize_theme
  58. end
  59. 1 def initialize_theme style_item_names=nil
  60. add_subfield :colors, type_id: Card::ScssID
  61. add_variables_subfield
  62. add_stylesheets_subfield style_item_names
  63. end
  64. 1 def add_stylesheets_subfield style_items=nil
  65. opts = { type_id: Card::SkinID }
  66. if theme_name
  67. theme_style = add_bootswatch_subfield
  68. opts[:content] = "[[#{theme_style.name}]]"
  69. end
  70. if style_items
  71. opts[:content] = [opts[:content], style_items].flatten.compact.to_pointer_content
  72. end
  73. add_subfield :stylesheets, opts
  74. end
  75. 1 def add_variables_subfield
  76. theme_content = content_from_theme(:variables)
  77. default_content = read_bootstrap_variables
  78. add_subfield :variables,
  79. type_id: Card::ScssID,
  80. content: "#{theme_content}\n\n\n#{default_content}"
  81. end
  82. 1 def add_bootswatch_subfield
  83. add_subfield :bootswatch, type_id: Card::ScssID,
  84. content: content_from_theme(:bootswatch)
  85. end
  86. 1 def theme_card
  87. @theme_card ||= theme_codename ? Card[theme_codename] : nil
  88. end
  89. 1 def content_from_theme subfield
  90. theme_card&.scss_from_theme_file subfield
  91. end
  92. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  93. 1 def edit_fields
  94. [[:colors, { title: "" }],
  95. [:variables, { title: "Variables" }],
  96. [:stylesheets, { title: "Styles" }]]
  97. end
  98. 1 view :one_line_content do
  99. ""
  100. end
  101. end
  102. end;end;end;end;
  103. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/customized_bootswatch_skin.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/type/customized_bootswatch_skin/html_views.rb

65.22% lines covered

23 relevant lines. 15 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Type; module CustomizedBootswatchSkin;
  3. # Set: All "CustomizedBootswatchSkin+HtmlViews" cards (HtmlViews)
  4. #
  5. 1 module HtmlViews;
  6. 1 extend Card::Set
  7. 2 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/customized_bootswatch_skin/html_views.rb"; end
  8. 1 include_set Abstract::Media
  9. 1 include_set Abstract::BsBadge
  10. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  11. 1 view :menu do
  12. ""
  13. end
  14. 1 def short_content
  15. ""
  16. # labeled_badge card.item_count, "items"
  17. # "#{card.item_count} items"
  18. end
  19. 1 view :core, template: :haml
  20. 1 info_bar_cols 6, 3, 3
  21. 1 before :bar do
  22. super()
  23. voo.show :edit_button, :bar_middle
  24. class_up "bar-middle", "p-3 align-items-center p-0"
  25. end
  26. 1 view :bar_right do
  27. render(:short_content)
  28. end
  29. 1 before :bar_nav do
  30. voo.hide :edit_link
  31. end
  32. 1 view :bar_bottom do
  33. render_core
  34. end
  35. end
  36. end;end;end;end;end;
  37. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type/customized_bootswatch_skin/html_views.rb ~~

card/tmpsets/set/mod017-card-mod-bootstrap/type_plus_right/customized_bootswatch_skin/colors.rb

44.23% lines covered

52 relevant lines. 23 lines covered and 29 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class TypePlusRight; module CustomizedBootswatchSkin;
  3. # Set: All "+Colors" cards on "CustomizedBootswatchSkin" cards
  4. #
  5. 1 module Colors;
  6. 1 extend Card::Set
  7. 3 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type_plus_right/customized_bootswatch_skin/colors.rb"; end
  8. VARIABLE_NAMES = {
  9. 1 colors: %i[blue indigo purple pink red orange yellow green teal cyan
  10. white gray-100 gray-200 gray-300 gray-400 gray-500 gray-600 gray-700 gray-800
  11. gray-900 black],
  12. theme_colors: %i[primary secondary success info warning danger light dark
  13. body-bg body-color]
  14. }.freeze
  15. # temporarily removed: link-color card-bg card-cap-bg
  16. # bootstrap default for link-color uses the theme-color function which
  17. # has to be defined between the theme-colors and that variable
  18. # (see bootstrap's _variables.scss)
  19. # TODO: deal with that
  20. # @param name [String] a scss variable name (it can start with a $)
  21. 1 def variable_value name
  22. value_from_scss(name, content) ||
  23. value_from_variables_card(name) ||
  24. default_value_from_bootstrap(name)
  25. end
  26. 1 def value_from_scss name, source
  27. name = name.to_s
  28. name = name[1..-1] if name.start_with?("$")
  29. source.match(definition_regex(name))&.capture(:value)
  30. end
  31. 1 def value_from_variables_card name
  32. return unless (var_card = left.variables_card) && var_card.content.present?
  33. value_from_scss name, var_card.content
  34. end
  35. 1 def definition_regex name
  36. /^(?<before>\s*\$#{name}\:\s*)(?<value>.+?)(?<after> !default;)$/
  37. end
  38. 1 def default_value_from_bootstrap name
  39. value_from_scss name, bootstrap_variables_scss
  40. end
  41. 1 def bootstrap_variables_scss
  42. @bootstrap_variables_scss ||= read_bootstrap_variables
  43. end
  44. 1 def colors
  45. @colors ||= variable_group_with_values :colors
  46. end
  47. 1 def theme_colors
  48. @theme_colors ||= variable_group_with_values :theme_colors
  49. end
  50. 1 def variable_group_with_values group
  51. VARIABLE_NAMES[group].each_with_object({}) do |name, h|
  52. h[name] = variable_value name
  53. end
  54. end
  55. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  56. 1 view :input, template: :haml do
  57. @colors = card.colors
  58. @theme_colors = card.theme_colors
  59. end
  60. 1 def theme_color_picker name, value
  61. # value = value[1..-1] if value.start_with? "$"
  62. options = VARIABLE_NAMES[:colors].map { |var| "$#{var}" }
  63. options << value unless options.include? value
  64. select_tag "theme_colors[#{name}]", options_for_select(options, value),
  65. class: "tags form-control"
  66. end
  67. 1 before :bar_right do
  68. voo.show :edit_button
  69. end
  70. 1 view :core, template: :haml do
  71. @colors = card.theme_colors.reject { |k, _v| k.in? %i[body-bg body-color] }
  72. end
  73. 1 view :bar_middle do
  74. <<-HTML
  75. <div class="colorpicker-element">
  76. <div class="input-group-addon">
  77. <span class="bg-body border p-1">Text</span>
  78. <span class="bg-dark text-light border p-1">Nav</span>
  79. <i class="bg-primary"></i>
  80. <i class="bg-secondary"></i>
  81. </div>
  82. </div>
  83. HTML
  84. end
  85. end
  86. 1 event :translate_variables_to_scss, :prepare_to_validate, on: :update do
  87. replace_values :colors
  88. replace_values :theme_colors
  89. end
  90. 1 def replace_values group, prefix=""
  91. values = variable_values_from_params group
  92. values.each_pair do |name, val|
  93. if content.match definition_regex(name)
  94. content.gsub! definition_regex(name), "\\k<before>#{prefix}#{val}\\k<after>"
  95. else
  96. self.content += "$#{name}: #{prefix}#{val} !default;\n"
  97. end
  98. end
  99. end
  100. 1 def variable_values_from_params group
  101. Env.params.dig(group)&.slice(*VARIABLE_NAMES[group]) || {}
  102. end
  103. end;end;end;end;end;
  104. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-bootstrap/set/type_plus_right/customized_bootswatch_skin/colors.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history.rb

78.57% lines covered

56 relevant lines. 44 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (History)
  4. #
  5. 1 module History;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history.rb"; end
  8. 1 event :update_ancestor_timestamps, :integrate do
  9. 216 ids = history_ancestor_ids
  10. 216 return unless ids.present?
  11. 29 Card.where(id: ids).update_all(updater_id: Auth.current_id, updated_at: Time.now)
  12. 58 ids.map { |anc_id| Card.expire anc_id.cardname }
  13. end
  14. # track history (acts, actions, changes) on this card
  15. 1 def history?
  16. 659 true
  17. end
  18. # all cards whose acts are considered part of this card's history
  19. 1 def history_card_ids
  20. nestee_ids << id
  21. end
  22. # all cards who are considered updated if this card's was updated
  23. 1 def history_parent_ids
  24. 490 nester_ids
  25. end
  26. 1 def history_ancestor_ids recursion_level=0
  27. 245 return [] if recursion_level > 5
  28. 245 ids = history_parent_ids +
  29. 29 history_parent_ids.map { |id| Card[id].history_ancestor_ids(recursion_level + 1) }
  30. 245 ids.flatten
  31. end
  32. # ~~FIXME~~: optimize (no need to instantiate all actions and changes!)
  33. # Nothing is instantiated here. ActiveRecord is much smarter than you think.
  34. # Methods like #empty? and #size make sql queries if their receivers are not already
  35. # loaded -pk
  36. 1 def first_change?
  37. # = update or delete
  38. 158 @current_action.action_type != :create && action_count == 2 &&
  39. create_action.card_changes.empty?
  40. end
  41. 1 def first_create?
  42. 158 @current_action.action_type == :create && action_count == 1
  43. end
  44. 1 def action_count
  45. 158 Card::Action.where(card_id: @current_action.card_id).count
  46. end
  47. # card has account that is responsible for prior acts
  48. 1 def has_edits?
  49. Card::Act.where(actor_id: id).where("card_id IS NOT NULL").present?
  50. end
  51. 1 def changed_fields
  52. 211 Card::Change::TRACKED_FIELDS & (changed_attribute_names_to_save | saved_changes.keys)
  53. end
  54. 1 def nestee_ids
  55. 78 requiring_id { @nestee_ids ||= nesting_ids(:referee_id, :referer_id) }
  56. end
  57. 1 def nester_ids
  58. 980 requiring_id { @nester_ids ||= nesting_ids(:referer_id, :referee_id) }
  59. end
  60. 1 def diff_args
  61. 2 { diff_format: :text }
  62. end
  63. # Delete all changes and old actions and make the last action the create action
  64. # (that way the changes for that action will be created with the first update)
  65. 1 def make_last_action_the_initial_action
  66. delete_all_changes
  67. old_actions.delete_all
  68. last_action.update! action_type: :create
  69. end
  70. 1 def clear_history
  71. delete_all_changes
  72. delete_old_actions
  73. end
  74. 1 def delete_old_actions
  75. old_actions.delete_all
  76. end
  77. 1 def delete_all_changes
  78. Card::Change.where(card_action_id: all_action_ids).delete_all
  79. end
  80. 1 def save_content_draft content
  81. super
  82. acts.create do |act|
  83. act.ar_actions.build(draft: true, card_id: id, action_type: :update)
  84. .card_changes.build(field: :db_content, value: content)
  85. end
  86. end
  87. 1 private
  88. 1 def nesting_ids return_field, where_field
  89. 236 Card::Reference.select(return_field).distinct.where(
  90. ref_type: "I", where_field => id
  91. ).pluck(return_field).compact
  92. end
  93. 1 def requiring_id
  94. 529 id ? yield : (return [])
  95. end
  96. end;end;end;end;
  97. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history/act_listing.rb

55.88% lines covered

68 relevant lines. 38 lines covered and 30 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module History;
  3. # Set: All cards (History, ActListing)
  4. #
  5. 1 module ActListing;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/act_listing.rb"; end
  8. 1 ACTS_PER_PAGE = Card.config.acts_per_page
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 def act_from_context
  11. 2 if (act_id = params["act_id"])
  12. Act.find(act_id) || raise(Card::NotFound, "act not found")
  13. else
  14. 2 card.last_action.act
  15. end
  16. end
  17. # used (by history and recent)for rendering act lists with legend and paging
  18. #
  19. # @param acts [ActiveRecord::Relation] relation that will return acts objects
  20. # @param context [Symbol] :relative or :absolute
  21. # @param draft_legend [Symbol] :show or :hide
  22. 1 def acts_layout acts, context, draft_legend=:hide
  23. bs_layout container: false, fluid: false do
  24. html _render_act_legend(draft_legend => :draft_legend)
  25. row(12) { act_list acts, context }
  26. row(12) { act_paging acts, context }
  27. end
  28. end
  29. 1 def act_list acts, context
  30. act_accordion acts, context do |act, seq|
  31. fmt = context == :relative ? self : act.card.format(:html)
  32. fmt.act_listing act, seq, context
  33. end
  34. end
  35. 1 def act_listing act, seq=nil, context=nil
  36. 2 opts = act_listing_opts_from_params(seq)
  37. 2 opts[:slot_class] = "revision-#{act.id} history-slot list-group-item"
  38. 2 context ||= (params[:act_context] || :absolute).to_sym
  39. 2 act_renderer(context).new(self, act, opts).render
  40. end
  41. # TODO: consider putting all these under one top-level param, eg:
  42. # act: { seq: X, diff: [show/hide], action_view: Y }
  43. 1 def act_listing_opts_from_params seq
  44. 2 { act_seq: (seq || params["act_seq"]),
  45. 2 action_view: (params["action_view"] || "summary").to_sym,
  46. hide_diff: params["hide_diff"].to_s.strip == "true" }
  47. end
  48. 1 def act_accordion acts, context, &block
  49. accordion_group acts_for_accordion(acts, context, &block), nil, class: "clear-both"
  50. end
  51. 1 def acts_for_accordion acts, context
  52. clean_acts(current_page_acts(acts)).map do |act|
  53. with_act_seq(context, acts) do |seq|
  54. yield act, seq
  55. end
  56. end
  57. end
  58. 1 def with_act_seq context, acts
  59. yield(context == :absolute ? nil : current_act_seq(acts))
  60. end
  61. 1 def current_act_seq acts
  62. @act_seq = @act_seq ? (@act_seq -= 1) : act_list_starting_seq(acts)
  63. end
  64. 1 def clean_acts acts
  65. # FIXME: if we get rid of bad act data, this will not be necessary
  66. # The current
  67. acts.select(&:card)
  68. end
  69. 1 def current_page_acts acts
  70. acts.page(acts_page_from_params).per acts_per_page
  71. end
  72. 1 def act_list_starting_seq acts
  73. acts.size - (acts_page_from_params - 1) * acts_per_page
  74. end
  75. 1 def acts_per_page
  76. @acts_per_page || ACTS_PER_PAGE
  77. end
  78. 1 def acts_page_from_params
  79. @acts_page_from_params ||= params["page"].present? ? params["page"].to_i : 1
  80. end
  81. 1 def act_paging acts, context
  82. wrap_with :div, class: "slotter btn-sm" do
  83. acts = current_page_acts acts
  84. opts = { remote: true, theme: "twitter-bootstrap-4" }
  85. opts[:total_pages] = 10 if limited_paging? context
  86. paginate acts, opts
  87. end
  88. end
  89. 1 def limited_paging? context
  90. context == :absolute && Act.count > 1000
  91. end
  92. 1 def action_icon action_type, extra_class=nil
  93. 2 icon = case action_type
  94. when :create then :add_circle
  95. 2 when :update then :pencil
  96. when :delete then :remove_circle
  97. when :draft then :wrench
  98. end
  99. 2 icon_tag icon, extra_class
  100. end
  101. 1 private
  102. 1 def act_renderer context
  103. 2 case context
  104. when :absolute
  105. 2 Act::ActRenderer::AbsoluteActRenderer
  106. when :bridge
  107. Act::ActRenderer::BridgeActRenderer
  108. else # relative
  109. Act::ActRenderer::RelativeActRenderer
  110. end
  111. end
  112. end
  113. end;end;end;end;end;
  114. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/act_listing.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history/actions.rb

74.63% lines covered

67 relevant lines. 50 lines covered and 17 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module History;
  3. # Set: All cards (History, Actions)
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module Actions;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/actions.rb"; end
  9. 1 def all_action_ids
  10. Card::Action.where(card_id: id).pluck :id
  11. end
  12. 1 def action_from_id action_id
  13. 289 return unless action_id.is_a?(Integer) || action_id =~ /^\d+$/
  14. # if not an integer revision id is probably a mod (e.g. if you request
  15. # files/:logo/standard.png)
  16. 16 action = Action.fetch action_id
  17. 16 return unless action.card_id == id
  18. 16 action
  19. end
  20. 1 def old_actions
  21. actions.where("id != ?", last_action_id)
  22. end
  23. 1 def create_action
  24. 111 @create_action ||= actions.first
  25. end
  26. 1 def nth_action index
  27. 289 index = index.to_i
  28. 289 return unless id && index.positive?
  29. Action.where("draft is not true AND card_id = #{id}")
  30. .order(:id).limit(1).offset(index - 1).first
  31. end
  32. 1 def new_content_action_id
  33. 1157 return unless @current_action && current_action_changes_content?
  34. 156 @current_action.id
  35. end
  36. 1 def current_action_changes_content?
  37. 176 new_card? || @current_action.new_content? || db_content_is_changing?
  38. end
  39. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  40. 1 def action_from_context
  41. 2 if (action_id = voo.action_id || params[:action_id])
  42. 2 Action.fetch action_id
  43. else
  44. card.last_action
  45. end
  46. end
  47. 1 def action_content action, view_type
  48. 2 return "" unless action.present?
  49. 2 wrap do
  50. 2 [action_content_toggle(action, view_type),
  51. content_diff(action, view_type)]
  52. end
  53. end
  54. 1 def content_diff action, view_type
  55. 2 diff = action.new_content? && content_changes(action, view_type)
  56. 2 return "<i>empty</i>" unless diff.present?
  57. 2 diff
  58. end
  59. 1 def action_content_toggle action, view_type
  60. 2 return unless show_action_content_toggle?(action, view_type)
  61. toggle_action_content_link action, view_type
  62. end
  63. 1 def show_action_content_toggle? action, view_type
  64. 2 view_type == :expanded || action.summary_diff_omits_content?
  65. end
  66. 1 def toggle_action_content_link action, view_type
  67. other_view_type = view_type == :expanded ? :summary : :expanded
  68. css_class = "revision-#{action.card_act_id} float-right"
  69. link_to_view "action_#{other_view_type}",
  70. icon_tag(action_arrow_dir(view_type), class: "md-24"),
  71. class: css_class,
  72. path: { action_id: action.id, look_in_trash: true }
  73. end
  74. 1 def action_arrow_dir view_type
  75. view_type == :expanded ? :triangle_left : :triangle_right
  76. end
  77. 1 def revert_actions_link link_text, path_args, html_args={}
  78. 2 return unless card.ok? :update
  79. 2 path_args.reverse_merge! action: :update, look_in_trash: true, assign: true,
  80. card: { skip: :validate_renaming }
  81. 2 html_args.reverse_merge! remote: true, method: :post, rel: "nofollow", path: path_args
  82. 2 add_class html_args, "slotter"
  83. 2 link_to link_text, html_args
  84. end
  85. 1 def action_legend
  86. types = %i[create update delete]
  87. legend = types.map do |action_type|
  88. "#{action_icon(action_type)} #{action_type}d"
  89. end
  90. legend << _render_draft_legend if voo.show?(:draft_legend)
  91. "<small>Actions: #{legend.join ' | '}</small>"
  92. end
  93. 1 def content_legend
  94. legend = [Card::Content::Diff.render_added_chunk("Additions"),
  95. Card::Content::Diff.render_deleted_chunk("Subtractions")]
  96. "<small>Content changes: #{legend.join ' | '}</small>"
  97. end
  98. 1 def content_changes action, diff_type, hide_diff=false
  99. 2 if hide_diff
  100. action.raw_view
  101. else
  102. 2 action.content_diff diff_type
  103. end
  104. end
  105. end
  106. end;end;end;end;end;
  107. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/actions.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history/acts.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module History;
  3. # Set: All cards (History, Acts)
  4. #
  5. # all acts with actions on self and on cards included in self (ie, acts shown in history)
  6. 1 module Acts;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/acts.rb"; end
  9. 1 def history_acts
  10. @history_acts ||= Act.all_with_actions_on(history_card_ids, true).order id: :desc
  11. end
  12. 1 def draft_acts
  13. drafts.created_by(Card::Auth.current_id).map(&:act)
  14. end
  15. end;end;end;end;end;
  16. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/acts.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history/events.rb

88.0% lines covered

50 relevant lines. 44 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module History;
  3. # Set: All cards (History, Events)
  4. #
  5. # must be called on all actions and before :set_name, :process_subcards and
  6. 1 module Events;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/events.rb"; end
  9. # :validate_delete_children
  10. 1 event :assign_action, :initialize, when: :actionable? do
  11. 362 act = director.need_act
  12. 362 @current_action = Card::Action.create(
  13. card_act_id: act.id,
  14. action_type: action,
  15. 362 draft: (Env.params["draft"] == "true")
  16. )
  17. 362 if @supercard && @supercard != self
  18. 183 @current_action.super_action = @supercard.current_action
  19. end
  20. end
  21. # can we store an action? (can be overridden, eg in files)
  22. 1 def actionable?
  23. 691 history?
  24. end
  25. 1 event :detect_conflict, :validate, on: :update, when: :edit_conflict? do
  26. 2 errors.add :conflict, tr(:error_not_latest_revision)
  27. end
  28. 1 def edit_conflict?
  29. 78 last_action_id_before_edit &&
  30. last_action_id_before_edit.to_i != last_action_id &&
  31. 3 (la = last_action) &&
  32. la.act.actor_id != Auth.current_id
  33. end
  34. # stores changes in the changes table and assigns them to the current action
  35. # removes the action if there are no changes
  36. 1 event :finalize_action, :finalize, when: :finalize_action? do
  37. 174 if changed_fields.present?
  38. 158 @current_action.update! card_id: id
  39. # Note: #last_change_on uses the id to sort by date
  40. # so the changes for the create changes have to be created before the first change
  41. 158 store_card_changes_for_create_action if first_change?
  42. 158 store_card_changes unless first_create?
  43. # FIXME: a `@current_action.card` call here breaks specs in solid_cache_spec.rb
  44. 16 elsif @current_action.card_changes.reload.empty?
  45. 16 @current_action.delete
  46. 16 @current_action = nil
  47. end
  48. end
  49. # changes for the create action are stored after the first update
  50. 1 def store_card_changes_for_create_action
  51. 27 Card::Action.cache.delete "#{create_action.id}-changes"
  52. 27 store_each_history_field create_action.id do |field|
  53. 162 attribute_before_act field
  54. end
  55. end
  56. 1 def store_card_changes
  57. 37 store_each_history_field @current_action.id, changed_fields do |field|
  58. 49 self[field]
  59. end
  60. end
  61. 1 def store_each_history_field action_id, fields=nil
  62. 64 fields ||= Card::Change::TRACKED_FIELDS
  63. if false # Card::Change.supports_import?
  64. # attach.feature fails with this
  65. values = fields.map.with_index { |field, index| [index, yield(field), action_id] }
  66. Card::Change.import [:field, :value, :card_action_id], values #, validate: false
  67. else
  68. 64 fields.each do |field|
  69. 211 Card::Change.create field: field,
  70. value: yield(field),
  71. card_action_id: action_id
  72. end
  73. end
  74. end
  75. 1 def finalize_action?
  76. 216 actionable? && current_action
  77. end
  78. 1 event :rollback_actions, :prepare_to_validate, on: :update, when: :rollback_request? do
  79. update_args = process_revert_actions
  80. Env.params["revert_actions"] = nil
  81. update! update_args
  82. clear_drafts
  83. abort :success
  84. end
  85. 1 event :finalize_act, after: :finalize_action, when: :act_card? do
  86. 88 Card::Director.act.update! card_id: id
  87. end
  88. 1 event :remove_empty_act, :integrate_with_delay_final, when: :remove_empty_act? do
  89. # Card::Director.act.delete
  90. # Card::Director.act = nil
  91. end
  92. 1 def remove_empty_act?
  93. 216 act_card? && Director.act&.ar_actions&.reload&.empty?
  94. end
  95. end;end;end;end;end;
  96. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/events.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history/last.rb

65.45% lines covered

55 relevant lines. 36 lines covered and 19 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module History;
  3. # Set: All cards (History, Last)
  4. #
  5. 1 module Last;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/last.rb"; end
  8. 1 def acted_at
  9. last_act.acted_at
  10. end
  11. 1 def revised_at
  12. (last_action && (act = last_action.act) && act.acted_at) || Time.zone.now
  13. end
  14. 1 def last_change_on field, opts={}
  15. 26 action_id = extract_action_id(opts[:before] || opts[:not_after])
  16. # If there is only one action then there are no entries in the changes table,
  17. # so we can't do a sql search but the changes are accessible via the action.
  18. 26 if no_last_change? action_id, opts[:before]
  19. nil
  20. 26 elsif create_action_last_change? action_id
  21. create_action&.change field
  22. else
  23. 26 last_change_from_action_id action_id, field, opts
  24. end
  25. end
  26. 1 def no_last_change? action_id, before
  27. 26 before && action_id == create_action.id
  28. end
  29. 1 def create_action_last_change? action_id
  30. 26 action_id == create_action&.id || (!action_id && create_action&.sole?)
  31. end
  32. 1 def last_change_from_action_id action_id, field, opts
  33. 26 Change.joins(:action).where(
  34. last_change_sql_conditions(opts),
  35. card_id: id,
  36. action_id: action_id,
  37. field: Card::Change.field_index(field)
  38. ).order(:id).last
  39. end
  40. 1 def last_change_sql_conditions opts
  41. 26 cond = "card_actions.card_id = :card_id AND field = :field"
  42. 26 cond += " AND (draft is not true)" unless opts[:including_drafts]
  43. 26 operator = "<" if opts[:before]
  44. 26 operator = "<=" if opts[:not_after]
  45. 26 cond += " AND card_action_id #{operator} :action_id" if operator
  46. 26 cond
  47. end
  48. 1 def last_action_id
  49. 55 last_action&.id
  50. end
  51. 1 def last_action
  52. 96 actions.where("id IS NOT NULL").last
  53. end
  54. 1 def last_content_action
  55. last_change_on(:db_content)&.action
  56. end
  57. 1 def last_content_action_id
  58. last_change_on(:db_content)&.card_action_id
  59. end
  60. 1 def last_actor
  61. last_act.actor
  62. end
  63. 1 def last_act
  64. @last_act ||=
  65. if (action = last_action)
  66. last_act_on_self = acts.last
  67. act_of_last_action = action.act
  68. return act_of_last_action unless last_act_on_self
  69. return last_act_on_self unless act_of_last_action
  70. return last_act_on_self if act_of_last_action == last_act_on_self
  71. if last_act_on_self.acted_at > act_of_last_action.acted_at
  72. last_act_on_self
  73. else
  74. act_of_last_action
  75. end
  76. end
  77. end
  78. 1 def previous_action action_id
  79. return unless action_id
  80. action_index = actions.find_index { |a| a.id == action_id }
  81. all_actions[action_index - 1] if action_index.to_i.nonzero?
  82. end
  83. 1 private
  84. 1 def extract_action_id action_arg
  85. 26 action_arg.is_a?(Card::Action) ? action_arg.id : action_arg
  86. end
  87. end;end;end;end;end;
  88. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/last.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history/revision.rb

40.0% lines covered

40 relevant lines. 16 lines covered and 24 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module History;
  3. # Set: All cards (History, Revision)
  4. #
  5. 1 module Revision;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/revision.rb"; end
  8. 1 def revision action, before_action=false
  9. # a "revision" refers to the state of all tracked fields
  10. # at the time of a given action
  11. action = Card::Action.fetch(action) if action.is_a? Integer
  12. return unless action
  13. if before_action
  14. revision_before_action action
  15. else
  16. revision_attributes action
  17. end
  18. end
  19. 1 def revision_attributes action
  20. Card::Change::TRACKED_FIELDS.each_with_object({}) do |field, attr_changes|
  21. last_change = action.change(field) || last_change_on(field, not_after: action)
  22. attr_changes[field.to_sym] = (last_change ? last_change.value : self[field])
  23. end
  24. end
  25. 1 def revision_before_action action
  26. if (prev_action = action.previous_action)
  27. revision prev_action
  28. else
  29. { trash: true }
  30. end
  31. end
  32. 1 def rollback_request?
  33. 153 history? && actions_to_revert.any?
  34. end
  35. 1 def process_revert_actions revert_actions=nil
  36. revert_actions ||= actions_to_revert
  37. update_args = { subcards: {} }
  38. reverting_to_previous = Env.params["revert_to"] == "previous"
  39. revert_actions.each do |action|
  40. merge_revert_action! action, update_args, reverting_to_previous
  41. end
  42. update_args
  43. end
  44. 1 def actions_to_revert
  45. 115 if (act_id = Env.params["revert_act"])
  46. Act.find(act_id).actions
  47. else
  48. 115 explicit_actions_to_revert
  49. end
  50. end
  51. 1 def explicit_actions_to_revert
  52. 115 Array.wrap(Env.params["revert_actions"]).map do |a_id|
  53. Action.fetch(a_id) || nil
  54. end.compact
  55. end
  56. 1 def merge_revert_action! action, update_args, reverting_to_previous
  57. rev = action.card.revision(action, reverting_to_previous)
  58. rev.delete :name unless rev[:name] # handles null name field in compound cards
  59. if action.card_id == id
  60. update_args.merge! rev
  61. else
  62. update_args[:subcards][action.card.name] = rev
  63. end
  64. end
  65. end;end;end;end;end;
  66. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/revision.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history/selected.rb

100.0% lines covered

39 relevant lines. 39 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module History;
  3. # Set: All cards (History, Selected)
  4. #
  5. # if these aren't in a nested module, the methods just overwrite the base
  6. # methods, but we need a distinct module so that super will be able to refer to
  7. 1 module Selected;
  8. 1 extend Card::Set
  9. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/selected.rb"; end
  10. # the base methods.
  11. 1 def content
  12. 10015 @selected_action_id ? selected_content : super
  13. end
  14. 1 def content= value
  15. 391 @selected_content = nil
  16. 391 super
  17. end
  18. 1 def select_action_by_params params
  19. 289 action = nth_action(params[:rev]) || action_from_id(params[:rev_id])
  20. 289 return unless action
  21. 16 select_action action.id
  22. end
  23. 1 def select_action action_id
  24. 16 run_callbacks :select_action do
  25. 16 self.selected_action_id = action_id
  26. end
  27. end
  28. 1 def selected_action_id
  29. 33 @selected_action_id || (@current_action&.id) || last_action_id
  30. end
  31. 1 def selected_action_id= action_id
  32. 45 @selected_content = nil
  33. 45 @selected_action_id = action_id
  34. end
  35. 1 def selected_action
  36. 12 selected_action_id && Action.fetch(selected_action_id)
  37. end
  38. 1 def selected_content
  39. 131 @selected_content ||= content_at_time_of_selected_action || db_content
  40. end
  41. 1 def content_at_time_of_selected_action
  42. 22 lc = last_change_on(:db_content, not_after: @selected_action_id, including_drafts: true)
  43. 22 lc&.value
  44. end
  45. 1 def with_selected_action_id action_id
  46. 12 current_action_id = @selected_action_id
  47. 12 select_action_id action_id
  48. 12 result = yield
  49. 12 select_action_id current_action_id
  50. 12 result
  51. end
  52. 1 def select_action_id action_id
  53. 24 run_callbacks :select_action do
  54. 24 self.selected_action_id = action_id
  55. end
  56. end
  57. 1 def selected_content_action_id
  58. 1389 @selected_action_id || new_content_action_id || last_content_action_id
  59. end
  60. end;end;end;end;end;
  61. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/selected.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history/views.rb

59.09% lines covered

22 relevant lines. 13 lines covered and 9 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module History;
  3. # Set: All cards (History, Views)
  4. #
  5. # History views
  6. 1 module Views;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history/views.rb"; end
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 view :history, cache: :never do
  11. frame do
  12. class_up "d0-card-body", "history-slot"
  13. acts_layout card.history_acts, :relative, :show
  14. end
  15. end
  16. 1 view :act, cache: :never do
  17. 2 act_listing act_from_context
  18. end
  19. 1 view :act_legend do
  20. bs_layout do
  21. row md: [12, 12], lg: [7, 5] do
  22. col action_legend
  23. col content_legend, class: "text-right"
  24. end
  25. end
  26. end
  27. 1 view :draft_legend do
  28. "#{action_icon(:draft)} unsaved draft"
  29. end
  30. 1 view :action_summary do
  31. 2 action_content action_from_context, :summary
  32. end
  33. 1 view :action_expanded do
  34. action_content action_from_context, :expanded
  35. end
  36. end
  37. end;end;end;end;end;
  38. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history/views.rb ~~

card/tmpsets/set/mod018-card-mod-history/all/history_bridge.rb

31.71% lines covered

41 relevant lines. 13 lines covered and 28 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (HistoryBridge)
  4. #
  5. 1 module HistoryBridge;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-history/set/all/history_bridge.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :creator_credit,
  10. wrap: { div: { class: "text-muted creator-credit" } }, cache: :never do
  11. return "" unless card.real?
  12. "Created by #{nest card.creator, view: :link} "\
  13. "#{time_ago_in_words(card.created_at)} ago"
  14. end
  15. 1 view :updated_by, wrap: { div: { class: "text-muted" } } do
  16. return "" unless card.id
  17. updaters = Card.search(updater_of: { id: card.id })
  18. return "" unless updaters.present?
  19. updaters = updater_links updaters, others_target: Card.fetch(card, :editors)
  20. "Updated by #{updaters}"
  21. end
  22. 1 def updater_links updaters, item_view: :link, max_count: 3, others_target: card
  23. return "" unless updaters.present?
  24. total = updaters.size
  25. fetch_count = total > max_count ? max_count - 1 : max_count
  26. reduced = first_card(fetch_count).map { |c| nest c, view: item_view }
  27. if total > max_count
  28. reduced << link_to_card(others_target, "#{total - fetch_count} others")
  29. end
  30. reduced.to_sentence
  31. end
  32. 1 def acts_bridge_layout acts, context=:bridge
  33. output [
  34. _render_creator_credit,
  35. act_link_list(acts, context),
  36. act_paging(acts, context)
  37. ]
  38. end
  39. 1 def act_link_list acts, context
  40. items = acts_for_accordion(acts, context) do |act, seq|
  41. act_link_list_item act, seq, context
  42. end
  43. bridge_pills items
  44. end
  45. 1 def act_link_list_item act, seq=nil, _context=nil
  46. opts = act_listing_opts_from_params(seq)
  47. opts[:slot_class] = "revision-#{act.id} history-slot nav-item"
  48. act_renderer(:bridge).new(self, act, opts).bridge_link
  49. end
  50. 1 def act_list_group acts, context, &block
  51. list_group acts_for_accordion(acts, context, &block), class: "clear-both"
  52. end
  53. 1 view :bridge_act, cache: :never do
  54. opts = act_listing_opts_from_params(nil)
  55. act = act_from_context
  56. ar = act_renderer(:bridge).new(self, act, opts)
  57. class_up "action-list", "my-3"
  58. wrap_with_overlay title: ar.overlay_title, slot: breadcrumb_data("History") do
  59. act_listing(act, opts[:act_seq], :bridge)
  60. end
  61. end
  62. end
  63. end;end;end;end;
  64. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-history/set/all/history_bridge.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment.rb

78.13% lines covered

64 relevant lines. 50 lines covered and 14 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (Attachment)
  4. #
  5. 1 module Attachment;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment.rb"; end
  8. 1 attr_writer :empty_ok
  9. 1 def self.included host_class
  10. 2 host_class.extend CarrierWave::CardMount
  11. end
  12. 1 event :select_file_revision, after: :select_action do
  13. 45 attachment.retrieve_from_store!(attachment.identifier)
  14. end
  15. # we need a card id for the path so we have to update db_content when we have
  16. # an id
  17. 6 event :correct_identifier, :finalize, on: :create, when: proc { |c| !c.web? } do
  18. 5 update_column(:db_content, attachment.db_content)
  19. 5 expire
  20. end
  21. 1 event :save_original_filename, :prepare_to_store, on: :save, when: :file_ready_to_save? do
  22. 9 return unless @current_action
  23. 9 @current_action.update! comment: original_filename
  24. end
  25. 1 event :validate_file_exist, :validate, on: :create do
  26. 6 return if empty_ok?
  27. 6 if will_be_stored_as == :web
  28. errors.add "url is missing" if content.blank?
  29. 6 elsif !attachment.file.present?
  30. 1 errors.add attachment_name, "is missing"
  31. end
  32. end
  33. 10 event :write_identifier, after: :save_original_filename, when: proc { |c| !c.web? } do
  34. 26 self.content = attachment.db_content
  35. end
  36. 1 def file_ready_to_save?
  37. 9 attachment.file.present? &&
  38. !preliminary_upload? &&
  39. !save_preliminary_upload? &&
  40. attachment_is_changing?
  41. end
  42. 1 def item_names _args={} # needed for flexmail attachments. hacky.
  43. [name]
  44. end
  45. 1 def original_filename
  46. 18 return content.split("/").last if web?
  47. 18 attachment.original_filename
  48. end
  49. 1 def unfilled?
  50. 3 !attachment.present? && !save_preliminary_upload? && !subcards? && blank_content?
  51. end
  52. 1 def attachment_changed?
  53. send "#{attachment_name}_changed?"
  54. end
  55. 1 def attachment_is_changing?
  56. 1 send "#{attachment_name}_is_changing?"
  57. end
  58. 1 def attachment_before_act
  59. send "#{attachment_name}_before_act"
  60. end
  61. 1 def create_versions? _new_file
  62. true
  63. end
  64. 1 def empty_ok?
  65. 6 @empty_ok
  66. end
  67. 1 def assign_set_specific_attributes
  68. # reset content if we really have something to upload
  69. 1306 self.content = nil if set_specific[attachment_name.to_s].present?
  70. 1306 super
  71. end
  72. 1 def delete_files_for_action action
  73. 9 with_selected_action_id(action.id) do
  74. 9 attachment.file.delete
  75. 9 attachment.versions.each_value do |version|
  76. 28 version.file.delete
  77. end
  78. end
  79. end
  80. 1 def revision action, before_action=false
  81. return unless (result = super)
  82. result[:empty_ok] = true
  83. result
  84. end
  85. 1 def attachment_format ext
  86. 30 if ext.present? && attachment && (original_ext = attachment.extension.sub(/^\./, ""))
  87. 30 if ["file", original_ext].member? ext
  88. 30 original_ext
  89. elsif (exts = Mime::Types[attachment.content_type])
  90. if exts.find { |mt| mt.extensions.member? ext }
  91. ext
  92. else
  93. exts[0].extensions[0]
  94. end
  95. end
  96. end
  97. rescue => e
  98. Rails.logger.info "attachment_format issue: #{e.message}"
  99. nil
  100. end
  101. end;end;end;end;
  102. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/cloud.rb

36.99% lines covered

73 relevant lines. 27 lines covered and 46 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Attachment;
  3. # Set: Abstract (Attachment, Cloud)
  4. #
  5. 1 module Cloud;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/cloud.rb"; end
  8. 1 event :change_bucket_if_read_only, :initialize,
  9. on: :update, when: :change_bucket_if_read_only? do
  10. @new_storage_type = storage_type_from_config
  11. end
  12. 1 event :validate_storage_type_update, :validate, on: :update, when: :cloud? do
  13. # FIXME: make it possible to retrieve the file from cloud storage
  14. # to store it somewhere else. Currently, it only works to change the
  15. # storage type if a new file is provided
  16. # i.e. `update storage_type: :local` fails but
  17. # `update storage_type: :local, file: [file handle]` is ok
  18. return unless storage_type_changed? && !attachment_is_changing?
  19. errors.add :storage_type, tr(:moving_files_is_not_supported)
  20. end
  21. 1 def bucket
  22. 554 @bucket ||= cloud? && (new_card_bucket || bucket_from_content || bucket_from_config)
  23. end
  24. 1 def new_card_bucket
  25. return unless new_card?
  26. # If the file is assigned before the bucket option we have to
  27. # check if there is a bucket options in set_specific.
  28. # That happens for exmaple when the file appears before the bucket in the
  29. # options hash:
  30. # Card.create file: file_handle, bucket: "my_bucket"
  31. set_specific[:bucket] || set_specific["bucket"] || bucket_from_config
  32. end
  33. 1 def bucket_config
  34. @bucket_config ||= load_bucket_config
  35. end
  36. 1 def load_bucket_config
  37. return {} unless bucket
  38. bucket_config = Cardio.config.file_buckets&.dig(bucket.to_sym) || {}
  39. bucket_config.symbolize_keys!
  40. bucket_config[:credentials]&.symbolize_keys!
  41. # we don't want :attributes hash symbolized, so we can't use
  42. # deep_symbolize_keys
  43. ensure_bucket_config do
  44. load_bucket_config_from_env bucket_config
  45. end
  46. end
  47. 1 def ensure_bucket_config
  48. yield.tap do |config|
  49. require_configuration! config
  50. require_credentials! config
  51. end
  52. end
  53. 1 def require_configuration! config
  54. cant_find_in_bucket! "configuration" unless config.present?
  55. end
  56. 1 def require_credentials! config
  57. cant_find_in_bucket! "credentials" unless config[:credentials]
  58. end
  59. 1 def cant_find_in_bucket! need
  60. raise Card::Error, "couldn't find #{need} for bucket #{bucket}"
  61. end
  62. 1 def load_bucket_config_from_env config
  63. config ||= {}
  64. each_config_option_from_env do |key|
  65. replace_with_env_variable config, key
  66. end
  67. credential_config config do |cred_hash|
  68. load_bucket_credentials_from_env cred_hash
  69. end
  70. end
  71. 1 def credential_config config
  72. config[:credentials] ||= {}
  73. yield config[:credentials]
  74. config.delete :credentials if config[:credentials].blank?
  75. config
  76. end
  77. 1 def each_config_option_from_env
  78. CarrierWave::FileCardUploader::CONFIG_OPTIONS.each do |key|
  79. yield key unless key.in? %i[attributes credentials]
  80. end
  81. end
  82. 1 def load_bucket_credentials_from_env cred_config
  83. each_credential_from_env do |option|
  84. replace_with_env_variable cred_config, option, "credentials"
  85. end
  86. end
  87. 1 def each_credential_from_env
  88. regexp = credential_from_env_regexp
  89. ENV.each_key do |env_key|
  90. next unless (m = regexp.match env_key)
  91. yield m[:option].downcase.to_sym
  92. end
  93. end
  94. 1 def credential_from_env_regexp
  95. Regexp.new "^(?:#{bucket.to_s.upcase}_)?CREDENTIALS_(?<option>.+)$"
  96. end
  97. 1 def replace_with_env_variable config, option, prefix=nil
  98. env_key = [prefix, option].compact.join("_").upcase
  99. new_value = ENV["#{bucket.to_s.upcase}_#{env_key}"] || ENV[env_key]
  100. config[option] = new_value if new_value
  101. end
  102. 1 def bucket_from_content
  103. return unless content
  104. content.match(/^\((?<bucket>[^)]+)\)/) { |m| m[:bucket] }
  105. end
  106. 1 def bucket_from_config
  107. cnf = Cardio.config
  108. cnf.file_default_bucket || cnf.file_buckets&.keys&.first
  109. end
  110. 1 def change_bucket_if_read_only?
  111. 7 cloud? && bucket_config[:read_only] && attachment_is_changing?
  112. end
  113. 1 def bucket= value
  114. if @action == :update
  115. @new_bucket = value
  116. else
  117. @bucket = value
  118. end
  119. end
  120. end;end;end;end;end;
  121. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/cloud.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/coded.rb

56.25% lines covered

16 relevant lines. 9 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Attachment;
  3. # Set: Abstract (Attachment, Coded)
  4. #
  5. 1 module Coded;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/coded.rb"; end
  8. 1 event :lose_coded_status_on_update, :initialize, on: :update, when: :coded? do
  9. # unless explicit
  10. return if @new_mod
  11. @new_storage_type ||= storage_type_from_config
  12. end
  13. 1 event :validate_coded_storage_type, :validate, on: :save, when: :will_become_coded? do
  14. errors.add :storage_type, tr(:mod_argument_needed_to_save) unless mod || @new_mod
  15. errors.add :storage_type, tr(:codename_needed_for_storage) if codename.blank?
  16. end
  17. 1 def will_become_coded?
  18. 83 will_be_stored_as == :coded
  19. end
  20. 1 def mod= value
  21. if @action == :update && mod != value
  22. @new_mod = value.to_s
  23. else
  24. @mod = value.to_s
  25. end
  26. end
  27. end;end;end;end;end;
  28. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/coded.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/local.rb

82.14% lines covered

28 relevant lines. 23 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Attachment;
  3. # Set: Abstract (Attachment, Local)
  4. #
  5. 1 module Local;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/local.rb"; end
  8. 1 event :update_public_link_on_create, :integrate, on: :create, when: :local? do
  9. 5 update_public_link
  10. end
  11. 1 event :remove_public_link_on_delete, :integrate, on: :delete, when: :local? do
  12. remove_public_links
  13. end
  14. 1 event :update_public_link, after: :update_read_rule, when: :local? do
  15. 5 return if content.blank?
  16. 5 if who_can(:read).include? Card::AnyoneID
  17. 5 create_public_links
  18. else
  19. remove_public_links
  20. end
  21. end
  22. 1 private
  23. 1 def create_public_links
  24. 5 path = attachment.public_path
  25. 5 return if File.exist? path
  26. 5 FileUtils.mkdir_p File.dirname(path)
  27. 5 File.symlink attachment.path, path unless File.symlink? path
  28. 5 create_versions_public_links
  29. end
  30. 1 def create_versions_public_links
  31. 5 attachment.versions.each_value do |version|
  32. 16 next if File.symlink? version.public_path
  33. 16 File.symlink version.path, version.public_path
  34. end
  35. end
  36. 1 def remove_public_links
  37. symlink_dir = File.dirname attachment.public_path
  38. return unless Dir.exist? symlink_dir
  39. FileUtils.rm_rf symlink_dir
  40. end
  41. end;end;end;end;end;
  42. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/local.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/paths.rb

91.43% lines covered

35 relevant lines. 32 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Attachment;
  3. # Set: Abstract (Attachment, Paths)
  4. #
  5. 1 module Paths;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/paths.rb"; end
  8. 1 MOD_FILE_DIR = "file".freeze
  9. 1 def store_dir
  10. 73 will_become_coded? ? coded_dir(@new_mod) : upload_dir
  11. end
  12. 1 def retrieve_dir
  13. 2451 coded? ? coded_dir : upload_dir
  14. end
  15. # place for files of regular file cards
  16. 1 def upload_dir
  17. 371 id ? "#{files_base_dir}/#{id}" : tmp_upload_dir
  18. end
  19. # place for files of mod file cards
  20. 1 def coded_dir new_mod=nil
  21. 2153 dir = File.join mod_dir(new_mod), MOD_FILE_DIR, codename.to_s
  22. 2153 FileUtils.mkdir_p(dir) unless File.directory?(dir)
  23. 2153 dir
  24. end
  25. 1 def mod_dir new_mod=nil
  26. 2153 mod_name = new_mod || mod
  27. 2153 dir = Mod.dirs.path(mod_name) || (mod_name.to_sym == :test && "test")
  28. 2153 raise Error, "can't find mod \"#{mod_name}\"" unless dir
  29. 2153 dir
  30. end
  31. 1 def files_base_dir
  32. 554 dir = bucket ? bucket_config[:subdirectory] : Card.paths["files"].existent.first
  33. 554 dir || files_base_dir_configuration_error
  34. end
  35. 1 def files_base_dir_configuration_error
  36. raise StandardError,
  37. "missing directory for file cache (default is `files` in deck root)"
  38. end
  39. # used in the indentifier
  40. 1 def file_dir
  41. 1226 if coded?
  42. 1136 ":#{codename}"
  43. 90 elsif cloud?
  44. "(#{bucket})/#{file_id}"
  45. else
  46. 90 "~#{file_id}"
  47. end
  48. end
  49. 1 def public?
  50. who_can(:read).include? Card::AnyoneID
  51. end
  52. 1 def file_id
  53. 90 id? ? id : upload_cache_card.id
  54. end
  55. end;end;end;end;end;
  56. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/paths.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/storage_type.rb

70.21% lines covered

94 relevant lines. 66 lines covered and 28 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Attachment;
  3. # Set: Abstract (Attachment, StorageType)
  4. #
  5. 1 module StorageType;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/storage_type.rb"; end
  8. 1 attr_writer :bucket, :storage_type
  9. 1 event :storage_type_change, :store, on: :update, when: :storage_type_changed? do
  10. # carrierwave stores file if @cache_id is not nil
  11. attachment.cache_stored_file!
  12. # attachment.retrieve_from_cache!(attachment.cache_name)
  13. update_storage_attributes
  14. # next line might be necessary to move files to cloud
  15. # make sure that we get the new identifier
  16. # otherwise action_id will return wrong id for new identifier
  17. db_content_will_change!
  18. write_identifier
  19. end
  20. 1 event :validate_storage_type, :validate, on: :save do
  21. 10 unless known_storage_type? will_be_stored_as
  22. errors.add :storage_type, tr(
  23. :unknown_storage_type,
  24. new_storage_type: @new_storage_type
  25. )
  26. end
  27. end
  28. 1 def will_be_stored_as
  29. 99 @new_storage_type || storage_type
  30. end
  31. 1 def read_only?
  32. 1 web? || (cloud? && bucket_config[:read_only])
  33. end
  34. 1 def cloud?
  35. 1881 storage_type == :cloud
  36. end
  37. 1 def web?
  38. 1586 storage_type == :web
  39. end
  40. 1 def local?
  41. 5 storage_type == :local
  42. end
  43. 1 def coded?
  44. 8625 storage_type == :coded
  45. end
  46. 1 def remote_storage?
  47. 30 cloud? || web?
  48. end
  49. 1 def storage_type
  50. 14742 @storage_type ||=
  51. 1207 new_card? ? storage_type_from_config : storage_type_from_content
  52. end
  53. 1 def deprecated_mod_file?
  54. 4 content && (lines = content.split("\n")) && lines.size == 4
  55. end
  56. 1 def mod
  57. 3289 @mod ||= coded? && mod_from_content
  58. end
  59. 1 def mod_from_content
  60. 1149 if content =~ %r{^:[^/]+/([^.]+)}
  61. 1149 Regexp.last_match(1) # current mod_file format
  62. else
  63. mod_from_deprecated_content
  64. end
  65. end
  66. # old format is still used in card_changes
  67. 1 def mod_from_deprecated_content
  68. return if content =~ /^\~/
  69. return unless (lines = content.split("\n")) && lines.size == 4
  70. lines.last
  71. end
  72. 1 def storage_type_from_config
  73. 32 valid_storage_type ENV["FILE_STORAGE"] || Cardio.config.file_storage
  74. end
  75. 1 def valid_storage_type storage_type
  76. 32 storage_type.to_sym.tap do |type|
  77. 32 invalid_storage_type! type unless type.in? valid_storage_type_list
  78. end
  79. end
  80. 1 def valid_storage_type_list
  81. 32 CarrierWave::FileCardUploader::STORAGE_TYPES
  82. end
  83. 1 def invalid_storage_type! type
  84. raise Card::Error, tr(:error_invalid_storage_type, type: type)
  85. end
  86. 1 def storage_type_from_content
  87. 1189 case content
  88. when /^\(/ then :cloud
  89. when %r{/^https?\:/} then :web
  90. 36 when /^~/ then :local
  91. 1149 when /^\:/ then :coded
  92. else
  93. 4 if deprecated_mod_file?
  94. :coded
  95. else
  96. 4 storage_type_from_config
  97. end
  98. end
  99. end
  100. 1 def update_storage_attributes
  101. @mod = @new_mod if @new_mod
  102. @bucket = @new_bucket if @new_bucket
  103. @storage_type = @new_storage_type
  104. end
  105. 1 def storage_type_changed?
  106. 4 @new_bucket || (@new_storage_type && @new_storage_type != storage_type) || @new_mod
  107. end
  108. 1 def storage_type= value
  109. known_storage_type? value
  110. if @action == :update #&& storage_type != value
  111. # we cant update the storage type directly here
  112. # if we do then the uploader doesn't find the file we want to update
  113. @new_storage_type = value
  114. else
  115. @storage_type = value
  116. end
  117. end
  118. 1 def with_storage_options opts={}
  119. 1257 old_values = {}
  120. 1257 validate_temporary_storage_type_change opts[:storage_type]
  121. 1257 %i[storage_type mod bucket].each do |opt_name|
  122. 3771 next unless opts[opt_name]
  123. old_values[opt_name] = instance_variable_get "@#{opt_name}"
  124. instance_variable_set "@#{opt_name}", opts[opt_name]
  125. @temp_storage_type = true
  126. end
  127. 1257 yield
  128. ensure
  129. 1257 @temp_storage_type = false
  130. 1257 old_values.each do |key, val|
  131. instance_variable_set "@#{key}", val
  132. end
  133. end
  134. 1 def temporary_storage_type_change?
  135. 1040 @temp_storage_type
  136. end
  137. 1 def validate_temporary_storage_type_change new_storage_type=nil
  138. 1257 new_storage_type ||= @new_storage_type
  139. 1257 return unless new_storage_type
  140. unless known_storage_type? new_storage_type
  141. raise Error, tr(:unknown_storage_type, new_storage_type: new_storage_type)
  142. end
  143. if new_storage_type == :coded && codename.blank?
  144. raise Error, "codename needed for storage type :coded"
  145. end
  146. end
  147. 1 def known_storage_type? type=storage_type
  148. 10 type.in? CarrierWave::FileCardUploader::STORAGE_TYPES
  149. end
  150. end;end;end;end;end;
  151. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/storage_type.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/upload_cache.rb

100.0% lines covered

49 relevant lines. 49 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Attachment;
  3. # Set: Abstract (Attachment, UploadCache)
  4. #
  5. # action id of the cached upload
  6. 1 module UploadCache;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/upload_cache.rb"; end
  9. 1 attr_accessor :action_id_of_cached_upload
  10. 1 def actionable?
  11. 28 super || preliminary_upload?
  12. end
  13. 1 event :prepare_attachment, :prepare_to_validate, on: :save, when: :preliminary_upload? do
  14. 9 save_original_filename # save original filename as comment in action
  15. 9 write_identifier # set db_content
  16. # (needs original filename to determine extension)
  17. 9 store_attachment!
  18. 9 store_card_changes
  19. # finalize_action # create Card::Change entry for db_content
  20. 9 card_id = new_card? ? upload_cache_card.id : id
  21. 9 @current_action.update! draft: true, card_id: card_id
  22. 9 success << {
  23. 9 target: (new_card? ? upload_cache_card : self),
  24. type: type_name,
  25. view: "preview_editor",
  26. rev_id: current_action.id
  27. }
  28. 9 abort :success
  29. end
  30. 1 event :assign_attachment_on_create, :initialize,
  31. after: :assign_action, on: :create, when: :save_preliminary_upload? do
  32. 5 return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
  33. 5 upload_cache_card.selected_action_id = action.id
  34. 5 upload_cache_card.select_file_revision
  35. 5 assign_attachment upload_cache_card.attachment.file, action.comment
  36. end
  37. 1 event :assign_attachment_on_update, :initialize,
  38. after: :assign_action, on: :update, when: :save_preliminary_upload? do
  39. 3 return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
  40. 6 uploaded_file = with_selected_action_id(action.id) { attachment.file }
  41. 3 assign_attachment uploaded_file, action.comment
  42. end
  43. 1 def assign_attachment file, original_filename
  44. 8 send "#{attachment_name}=", file
  45. 8 write_identifier
  46. 8 @current_action&.update! comment: original_filename
  47. end
  48. 1 event :delete_cached_upload_file_on_create, :integrate,
  49. on: :create, when: :save_preliminary_upload? do
  50. 5 return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
  51. 5 upload_cache_card.delete_files_for_action action
  52. 5 action.delete
  53. end
  54. # at some point uploaded files of canceled file card creation
  55. # should be deleted. We do this when ever an new file is created.
  56. 1 event :clear_draft_files, :integrate_with_delay, priority: 100, on: :create do
  57. 5 Card.delete_tmp_files_of_cached_uploads
  58. end
  59. 1 event :delete_cached_upload_file_on_update, :integrate,
  60. on: :update, when: :save_preliminary_upload? do
  61. 3 return unless (action = Card::Action.fetch(@action_id_of_cached_upload))
  62. 3 delete_files_for_action action
  63. 3 action.delete
  64. end
  65. # used for uploads for new cards until the new card is created
  66. 1 def upload_cache_card
  67. 71 cache_card_codename = "new_#{attachment_name}"
  68. 71 @upload_cache_card ||= Card::Codename.card(cache_card_codename) { Card[:new_file] }
  69. end
  70. 1 def preliminary_upload?
  71. 32 Card::Env && Card::Env.params[:attachment_upload]
  72. end
  73. 1 def save_preliminary_upload?
  74. 37 @action_id_of_cached_upload.present?
  75. end
  76. # place for files if card doesn't have an id yet
  77. 1 def tmp_upload_dir _action_id=nil
  78. 22 "#{files_base_dir}/#{upload_cache_card.id}"
  79. end
  80. end;end;end;end;end;
  81. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/upload_cache.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/abstract/attachment/web.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Abstract; module Attachment;
  3. # Set: Abstract (Attachment, Web)
  4. #
  5. 1 module Web;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/web.rb"; end
  8. 1 def no_upload?
  9. 10 web? || storage_type_from_config == :web
  10. end
  11. end;end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/abstract/attachment/web.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/all/file_utils.rb

90.91% lines covered

22 relevant lines. 20 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (FileUtils)
  4. #
  5. 1 module FileUtils;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/all/file_utils.rb"; end
  8. 1 module ClassMethods
  9. 1 def update_all_storage_locations
  10. Card.search(type_id: ["in", Card::FileID, Card::ImageID])
  11. .each(&:update_storage_location!)
  12. end
  13. 1 def delete_tmp_files_of_cached_uploads
  14. 5 cards_with_disposable_attachments do |card, action|
  15. 1 card.delete_files_for_action action
  16. 1 action.delete
  17. end
  18. end
  19. 1 def cards_with_disposable_attachments
  20. 5 draft_actions_with_attachment.each do |action|
  21. # we don't want to delete uploads in progress
  22. 1 next unless old_enough?(action.created_at) && (card = action.card)
  23. # we can't delete attachments we don't have write access to
  24. 1 next if card.read_only?
  25. 1 yield card, action
  26. end
  27. end
  28. 1 def old_enough? time, expiration_time=5.day.to_i
  29. 1 Time.now - time > expiration_time
  30. end
  31. 1 def draft_actions_with_attachment
  32. 5 Card::Action.find_by_sql(
  33. "SELECT * FROM card_actions "\
  34. "INNER JOIN cards ON card_actions.card_id = cards.id "\
  35. "WHERE cards.type_id IN (#{Card::FileID}, #{Card::ImageID}) "\
  36. "AND card_actions.draft = true"
  37. )
  38. end
  39. 1 def count_cards_with_attachment
  40. Card.search type_id: ["in", Card::FileID, Card::ImageID], return: :count
  41. end
  42. end
  43. end;end;end;end;
  44. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/all/file_utils.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/self/admin.rb

60.0% lines covered

10 relevant lines. 6 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Admin"
  4. #
  5. 1 module Admin;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/admin.rb"; end
  8. 1 add_to_basket(
  9. :tasks,
  10. name: :update_file_storage_locations,
  11. execute_policy: -> { Card.update_all_storage_locations },
  12. stats: {
  13. title: "cards with attachment",
  14. count: -> { Card.count_cards_with_attachment }
  15. # link_text: "update storage locations",
  16. # task: "update_file_storage_locations"
  17. }
  18. )
  19. 1 add_to_basket(
  20. :tasks,
  21. name: :delete_upload_tmp_files,
  22. execute_policy: -> { Card.delete_tmp_files_of_cached_uploads },
  23. stats: {
  24. title: "tmp files of canceled uploads",
  25. count: -> { ::Card.draft_actions_with_attachment },
  26. link_text: "delete tmp files",
  27. task: "delete_tmp_files_of_cached_uploads"
  28. }
  29. )
  30. end;end;end;end;
  31. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/admin.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/self/favicon.rb

92.31% lines covered

13 relevant lines. 12 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Favicon"
  4. #
  5. 1 module Favicon;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/favicon.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :source do
  10. 224 source = card.type_id == Card::ImageID ? super() : nil
  11. 224 source.present? ? source : nest(:logo, view: :source, size: voo.size)
  12. end
  13. 1 view :link_tag, perms: :none do
  14. 224 return unless (source = render :source, size: :small)
  15. 224 tag :link, rel: "shortcut icon", href: source
  16. end
  17. 1 def raw_help_text
  18. "A favicon (or shortcut icon) is a small image used by browsers to help identify "\
  19. "your website. [[http://www.decko.org/favicon|How to customize your favicon]]"
  20. end
  21. end
  22. end;end;end;end;
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/favicon.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/self/new_file.rb

72.73% lines covered

11 relevant lines. 8 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "NewFile"
  4. #
  5. 1 module NewFile;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/new_file.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :source, cache: :never do
  10. super()
  11. end
  12. 1 view :core, cache: :never do
  13. super()
  14. end
  15. 1 view :input, cache: :never do
  16. super()
  17. end
  18. end
  19. end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/new_file.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/self/new_image.rb

90.91% lines covered

11 relevant lines. 10 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "NewImage"
  4. #
  5. 1 module NewImage;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/new_image.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :source, cache: :never do
  10. 4 super()
  11. end
  12. 1 view :core, cache: :never do
  13. 4 super()
  14. end
  15. 1 view :input, cache: :never do
  16. super()
  17. end
  18. end
  19. end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/self/new_image.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/type/file.rb

90.91% lines covered

66 relevant lines. 60 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "File" cards
  4. #
  5. 1 module File;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/file.rb"; end
  8. 1 attachment :file, uploader: CarrierWave::FileCardUploader
  9. 1 module SelectedAction
  10. 1 def select_action_by_params params
  11. # skip action table lookups for current revision
  12. 51 rev_id = params[:rev_id]
  13. 51 super unless rev_id && rev_id == last_content_action_id
  14. end
  15. 1 def last_content_action_id
  16. 1040 return super if temporary_storage_type_change?
  17. # find action id from content (saves lookups)
  18. 1040 db_content.to_s.split(%r{[/\.]})[-2]
  19. end
  20. end
  21. 1 include SelectedAction
  22. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  23. 1 view :source do
  24. 1 file = card.attachment
  25. 1 return "" unless file.valid?
  26. 1 contextualize_path file.url
  27. end
  28. 1 view :core do
  29. handle_source do |source|
  30. card_url source
  31. end
  32. end
  33. 1 def short_content
  34. number_to_human_size card.attachment.size
  35. end
  36. 1 def handle_source
  37. 38 source = _render_source
  38. 38 return "" if source.blank?
  39. 38 block_given? ? yield(source) : source
  40. rescue => e
  41. Rails.logger.info "Error with file source: #{e.message}"
  42. tr :file_error
  43. end
  44. 1 def selected_version
  45. 2 card.attachment
  46. end
  47. end
  48. 2 module FileFormat; module_parent.send :register_set_format, Card::Format::FileFormat, self; extend Card::Set::AbstractFormat
  49. # NOCACHE because returns send_file args. not in love with this...
  50. 1 view :core, cache: :never do
  51. # this means we only support known formats. dislike.
  52. 30 attachment_format = card.attachment_format(params[:format])
  53. 30 return _render_not_found unless attachment_format
  54. 30 return card.format(:html).render_core if card.remote_storage?
  55. 30 set_response_headers
  56. 30 args_for_send_file
  57. end
  58. 1 def args_for_send_file
  59. 30 file = selected_version
  60. 30 [file.path, { type: file.content_type,
  61. filename: "#{card.name.safe_key}#{file.extension}",
  62. x_sendfile: true,
  63. 30 disposition: (params[:format] == "file" ? "attachment" : "inline") }]
  64. end
  65. 1 def set_response_headers
  66. 30 return unless params[:explicit_file] && (response = controller&.response)
  67. 30 response.headers["Expires"] = 1.year.from_now.httpdate
  68. # currently using default "private", because proxy servers could block
  69. # needed permission checks
  70. # r.headers["Cache-Control"] = "public"
  71. end
  72. end
  73. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  74. 1 view :core do
  75. 1 handle_source do |source|
  76. 1 "<a href=\"#{source}\">#{tr :download, title: title_in_context(voo.title)}</a>"
  77. end
  78. end
  79. 1 view :input do
  80. 10 if card.no_upload?
  81. text_field :content, class: "d0-card-content"
  82. else
  83. 10 haml :file_chooser, action_text: file_chooser_action_text
  84. end
  85. end
  86. 1 view :preview_editor, unknown: true, cache: :never do
  87. 9 haml :preview_editor
  88. end
  89. 1 def file_chooser_action_text
  90. 10 action = card.new_card? ? "Add" : "Replace"
  91. 10 "#{action} #{humanized_attachment_name}..."
  92. end
  93. 1 def humanized_attachment_name
  94. 10 card.attachment_name.to_s.humanize
  95. end
  96. 1 def preview
  97. 4 ""
  98. end
  99. 1 def cached_upload_card_name
  100. 9 Card::Env.params[:attachment_upload].gsub(/\[\w+\]$/, "[action_id_of_cached_upload]")
  101. end
  102. 1 def preview_editor_delete_text
  103. 9 tr :delete
  104. end
  105. end
  106. end;end;end;end;
  107. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/file.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/type/image.rb

78.33% lines covered

60 relevant lines. 47 lines covered and 13 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Image" cards
  4. #
  5. 1 module Image;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/image.rb"; end
  8. 1 attachment :image, uploader: CarrierWave::ImageCardUploader
  9. 1 include File::SelectedAction
  10. 1 def create_versions? new_file
  11. 2356 new_file.extension != "svg"
  12. end
  13. 1 def svg?
  14. 261 image&.extension == ".svg"
  15. end
  16. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  17. 1 include File::Format
  18. 1 view :one_line_content do
  19. _render_core size: :icon
  20. end
  21. 1 def short_content
  22. 7 render_core size: :icon
  23. end
  24. 1 view :source do
  25. 261 return card.content if card.web?
  26. 261 image = selected_version
  27. 261 return "" unless image.valid?
  28. 261 contextualize_path image.url
  29. end
  30. 1 def selected_version
  31. 289 size = determine_image_size
  32. 289 if size && size != :original
  33. 289 card.image.versions[size]
  34. else
  35. card.image
  36. end
  37. end
  38. 1 def handle_source
  39. 37 super
  40. end
  41. 1 def closed_size
  42. :icon
  43. end
  44. 1 def main_size
  45. 7 :large
  46. end
  47. 1 def default_size
  48. 5 :medium
  49. end
  50. 1 def determine_image_size
  51. 289 voo.size =
  52. case
  53. when nest_mode == :closed then closed_size
  54. 277 when voo.size.present? then voo.size.to_sym
  55. 7 when main? then main_size
  56. 5 else default_size
  57. end
  58. 289 voo.size = :original if voo.size == :full
  59. 289 voo.size
  60. end
  61. 1 view :inline do
  62. _render_core
  63. end
  64. end
  65. 2 module EmailHtmlFormat; module_parent.send :register_set_format, Card::Format::EmailHtmlFormat, self; extend Card::Set::AbstractFormat
  66. 1 view :inline, cache: :never do
  67. handle_source do |source|
  68. return source unless (mail = inherit :active_mail) &&
  69. ::File.exist?(path = selected_version.path)
  70. url = attach_image mail, path
  71. image_tag url
  72. end
  73. end
  74. 1 def attach_image mail, path
  75. mail.attachments.inline[path] = ::File.read path
  76. mail.attachments[path].url
  77. end
  78. end
  79. 2 module CssFormat; module_parent.send :register_set_format, Card::Format::CssFormat, self; extend Card::Set::AbstractFormat
  80. 1 view :core do
  81. handle_source
  82. end
  83. 1 view :content do # why is this necessary?
  84. render_core
  85. end
  86. end
  87. 2 module FileFormat; module_parent.send :register_set_format, Card::Format::FileFormat, self; extend Card::Set::AbstractFormat
  88. 1 include File::FileFormat
  89. end
  90. end;end;end;end;
  91. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/image.rb ~~

card/tmpsets/set/mod019-card-mod-carrierwave/type/image/html_views.rb

64.1% lines covered

39 relevant lines. 25 lines covered and 14 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Type; module Image;
  3. # Set: All "Image+HtmlViews" cards (HtmlViews)
  4. #
  5. 1 module HtmlViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/image/html_views.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 include File::HtmlFormat
  10. # core HTML image view.
  11. 1 view :core do
  12. 261 return card.attachment.read if card.svg?
  13. 37 with_valid_source do |source|
  14. 37 image_tag source, alt: card.name
  15. end
  16. end
  17. 1 def with_valid_source
  18. 37 handle_source do |source|
  19. 37 if source.blank? || source == "missing"
  20. # FIXME: these images should be "broken", not "missing"
  21. invalid_image source
  22. else
  23. 37 yield source
  24. # consider title..
  25. end
  26. end
  27. end
  28. 1 view :full_width do
  29. with_valid_source do |source|
  30. image_tag source, alt: card.name, class: "w-100"
  31. end
  32. end
  33. 1 def invalid_image source
  34. # ("missing" is the view for "unknown" now, so we shouldn't further confuse things)
  35. "<!-- invalid image for #{safe_name}; source: #{source} -->"
  36. end
  37. 1 def preview
  38. 15 return if card.new_card? && !card.preliminary_upload?
  39. 11 wrap_with :div, class: "attachment-preview",
  40. id: "#{card.attachment.filename}-preview" do
  41. 11 _render_core size: :medium
  42. end
  43. end
  44. 1 def show_action_content_toggle? _action, _view_type
  45. true
  46. end
  47. 1 view :content_changes do
  48. content_changes card.last_action, :expanded
  49. end
  50. 1 def content_changes action, diff_type, hide_diff=false
  51. voo.size = diff_type == :summary ? :icon : :medium
  52. [old_image(action, hide_diff), new_image(action)].compact.join
  53. end
  54. 1 def old_image action, hide_diff
  55. return if hide_diff || !action
  56. return unless (last_change = card.last_change_on(:db_content, before: action))
  57. card.with_selected_action_id last_change.card_action_id do
  58. Card::Content::Diff.render_deleted_chunk _render_core
  59. end
  60. end
  61. 1 def new_image action
  62. card.with_selected_action_id action.id do
  63. Card::Content::Diff.render_added_chunk _render_core
  64. end
  65. end
  66. end
  67. end;end;end;end;end;
  68. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-carrierwave/set/type/image/html_views.rb ~~

card/tmpsets/set/mod020-card-mod-date/all/calendar.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Calendar)
  4. #
  5. 1 module Calendar;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/all/calendar.rb"; end
  8. 1 Self::InputOptions.add_to_basket :options, "calendar"
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 def calendar_input
  11. text_field :content, class: "date-editor datetimepicker-input",
  12. "data-toggle": "datetimepicker",
  13. "data-target": "##{form_prefix}_content.date-editor"
  14. end
  15. end
  16. end;end;end;end;
  17. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/all/calendar.rb ~~

card/tmpsets/set/mod020-card-mod-date/self/datepicker.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Datepicker"
  4. #
  5. 1 module Datepicker;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/self/datepicker.rb"; end
  8. 1 def raw_help_text
  9. <<-TEXT
  10. Configure the date select tool using these available
  11. [[https://tempusdominus.github.io/bootstrap-4/Options/|options]]
  12. TEXT
  13. end
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/self/datepicker.rb ~~

card/tmpsets/set/mod020-card-mod-date/self/script_datepicker.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptDatepicker"
  4. #
  5. 1 module ScriptDatepicker;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/self/script_datepicker.rb"; end
  8. 1 include_set Abstract::VendorCodeFile
  9. 1 Self::ScriptMods.add_item :script_datepicker
  10. 1 def source_files
  11. %w[moment/min/moment-with-locales.min.js
  12. tempusdominus/build/js/tempusdominus-bootstrap-4.js]
  13. end
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/self/script_datepicker.rb ~~

card/tmpsets/set/mod020-card-mod-date/self/script_datepicker_config.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptDatepickerConfig"
  4. #
  5. 1 module ScriptDatepickerConfig;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/self/script_datepicker_config.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptEditors.add_item :script_datepicker_config
  10. 1 All::Head::HtmlFormat.add_to_basket :mod_js_config, [:datepicker, "setDatepickerConfig"]
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/self/script_datepicker_config.rb ~~

card/tmpsets/set/mod020-card-mod-date/self/style_datepicker.rb

80.0% lines covered

10 relevant lines. 8 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleDatepicker"
  4. #
  5. 1 module StyleDatepicker;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/self/style_datepicker.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::StyleLibraries.add_item :style_datepicker
  10. 1 def source_files
  11. %w[lib/stylesheets/tempusdominus.scss
  12. vendor/tempusdominus/src/sass/_tempusdominus-bootstrap-4.scss]
  13. end
  14. 1 def source_dir
  15. ""
  16. end
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/self/style_datepicker.rb ~~

card/tmpsets/set/mod020-card-mod-date/type/date.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Date" cards
  4. #
  5. 1 module Date;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-date/set/type/date.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def input_type
  10. :calendar
  11. end
  12. end
  13. end;end;end;end;
  14. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-date/set/type/date.rb ~~

card/tmpsets/set/mod022-card-mod-follow/abstract/follow_option.rb

93.1% lines covered

29 relevant lines. 27 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Abstract
  3. # Set: Abstract (FollowOption)
  4. #
  5. 1 module FollowOption;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/abstract/follow_option.rb"; end
  8. 1 def restrictive_option?
  9. Card::FollowOption.restrictive_options.include? codename
  10. end
  11. 1 def description set_card
  12. 34 set_card.follow_label
  13. end
  14. # follow option methods on the Card class
  15. # FIXME: there's not a great reason to have these on the Card class
  16. 1 module ClassMethods
  17. # args:
  18. # position: <Fixnum> (starting at 1, default: add to end)
  19. 1 def restrictive_follow_opts args
  20. 2 add_option args, :restrictive
  21. end
  22. # args:
  23. # position: <Fixnum> (starting at 1, default: add to end)
  24. 1 def follow_opts args
  25. 2 add_option args, :main
  26. end
  27. 1 def follow_test opts={}, &block
  28. 2 Card::FollowOption.test[get_codename(opts)] = block
  29. end
  30. 1 def follower_candidate_ids opts={}, &block
  31. 2 Card::FollowOption.follower_candidate_ids[get_codename(opts)] = block
  32. end
  33. 1 private
  34. 1 def insert_option pos, item, type
  35. 4 list = Card::FollowOption.codenames(type)
  36. 4 list[pos] ? list.insert(pos, item) : (list[pos] = item)
  37. # If pos > codenames.size in a previous insert then we have a bunch
  38. # of preceding nils in the array.
  39. # Hence, we have to overwrite a nil value if we encounter one and
  40. # can't use insert.
  41. end
  42. 1 def add_option opts, type, &_block
  43. 4 codename = get_codename opts
  44. 4 if opts[:position]
  45. 4 insert_option opts[:position] - 1, codename, type
  46. else
  47. Card::FollowOption.codenames(type) << codename
  48. end
  49. 4 Card::FollowOption.codenames(:all) << codename
  50. end
  51. 1 def get_codename opts
  52. 8 opts[:codename] || name.match(/::(\w+)$/)[1].underscore.to_sym
  53. end
  54. end
  55. end;end;end;end;
  56. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/abstract/follow_option.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/follow.rb

70.0% lines covered

20 relevant lines. 14 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Follow)
  4. #
  5. # for override
  6. 1 module Follow;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow.rb"; end
  9. 1 def followable?
  10. 89 true
  11. end
  12. 1 def follow_label
  13. name
  14. end
  15. 1 def list_direct_followers?
  16. false
  17. end
  18. 1 def follow_option?
  19. codename && FollowOption.codenames.include?(codename)
  20. end
  21. # the set card to be followed if you want to follow changes of card
  22. 1 def follow_set_card
  23. 4 Card.fetch name, :self
  24. end
  25. 1 def follow_rule_name user=nil
  26. follow_set_card&.follow_rule_name user
  27. end
  28. 1 def follow_rule_card user=nil, args={}
  29. Card.fetch follow_rule_name(user), args
  30. end
  31. 1 def follow_rule? user=nil
  32. Card.exists? follow_rule_name(user)
  33. end
  34. end;end;end;end;
  35. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/follow/follow_link.rb

50.0% lines covered

30 relevant lines. 15 lines covered and 15 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Follow)
  4. #
  5. #! no set module
  6. 1 module Follow;
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follow_link.rb"; end
  8. 1 class FollowLink
  9. 1 attr_reader :format, :rule_content, :link_text, :action, :css_class, :hover_text
  10. 1 delegate :link_to, to: :format
  11. 1 def initialize format
  12. @format = format
  13. @card = format.card
  14. end
  15. 1 def modal_link icon=false
  16. opts = link_opts.merge(
  17. "data-path": link_opts[:path],
  18. "data-toggle": "modal",
  19. "data-target": "#modal-#{card.name.safe_key}",
  20. class: css_classes("follow-link", link_opts[:class])
  21. )
  22. link_to render_link_text(icon), opts
  23. end
  24. 1 def button
  25. opts = link_opts(:follow_section).merge(
  26. remote: true,
  27. class: @format.css_classes("follow-link", link_opts[:class],
  28. "slotter btn btn-sm btn-primary")
  29. )
  30. opts["data-update-foreign-slot"] = ".d0-card-body > .card-slot.RIGHT-Xfollower.content-view"
  31. opts["data-hover-text"] = hover_text if hover_text
  32. link_to render_link_text, opts
  33. end
  34. 1 def link_opts success_view=:follow_status
  35. { title: title,
  36. path: path(success_view),
  37. class: css_class }
  38. end
  39. 1 def render_link_text icon=false
  40. verb = %(<span class="follow-verb">#{link_text}</span>)
  41. icon = icon ? icon_tag(:flag) : ""
  42. [icon, verb].compact.join.html_safe
  43. end
  44. 1 private
  45. 1 def mark
  46. @card.follow_set_card.follow_rule_name Auth.current.name
  47. end
  48. 1 def path view=:follow_status
  49. @format.path mark: mark,
  50. action: :update,
  51. success: { id: @card.name, view: view },
  52. card: { content: "[[#{rule_content}]]" }
  53. end
  54. 1 def title
  55. "#{action} emails about changes to #{@card.follow_label}"
  56. end
  57. end
  58. end;end;end;end;
  59. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follow_link.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/follow/follow_link_views.rb

68.42% lines covered

19 relevant lines. 13 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Follow;
  3. # Set: All cards (Follow, FollowLinkViews)
  4. #
  5. 1 module FollowLinkViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follow_link_views.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 def follow_link_class
  10. card.followed? ? StopFollowLink : StartFollowLink
  11. end
  12. 1 def show_follow?
  13. Auth.signed_in? && !card.new_card? && card.followable?
  14. end
  15. end
  16. 2 module JsonFormat; module_parent.send :register_set_format, Card::Format::JsonFormat, self; extend Card::Set::AbstractFormat
  17. 1 view :follow_status do
  18. follow_link_class.link_opts
  19. end
  20. end
  21. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  22. 1 def follow_button
  23. follow_link_class.new(self).button
  24. end
  25. 1 def follow_modal_link
  26. follow_link_class.new(self).modal_link
  27. end
  28. 1 view :follow_button, cache: :never do
  29. follow_button
  30. end
  31. end
  32. end;end;end;end;end;
  33. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follow_link_views.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/follow/followed_by.rb

73.68% lines covered

38 relevant lines. 28 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Follow;
  3. # Set: All cards (Follow, FollowedBy)
  4. #
  5. # used by +:followers overwritten in type/set.rb and type/cardtype.rb
  6. 1 module FollowedBy;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/followed_by.rb"; end
  9. 1 def followed?
  10. followed_by? Auth.current_id
  11. end
  12. # for sets and cardtypes it doesn't check whether the users is following the
  13. # card itself instead it checks whether he is following the complete set
  14. 1 def followed_by? user_id
  15. follow_rule_applies?(user_id) || left&.followed_by_as_field?(self, user_id)
  16. end
  17. 1 def followed_by_as_field? field, user_id
  18. followed_field?(field) && followed_by?(user_id)
  19. end
  20. # returns true if according to the follow_field_rule followers of self also
  21. # follow changes of field_card
  22. 1 def followed_field? field_card
  23. return unless (follow_field_rule = rule_card(:follow_fields))
  24. follow_field_rule.item_names(context: self).find do |item|
  25. case item.to_name.key
  26. when field_card.key then true
  27. when :nests.cardname.key then nested_card?(field_card)
  28. end
  29. end
  30. end
  31. 1 def nested_card? card
  32. nestee_ids.include? card.id
  33. end
  34. ## the following methods all handle _explicit_ (direct) follow rules (not fields)
  35. 1 def follow_rule_applies? follower_id
  36. !follow_rule_option(follower_id).nil?
  37. end
  38. 1 def follow_rule_option follower_id
  39. 365 all_follow_rule_options(follower_id).find do |option|
  40. 700 follow_rule_option_applies? follower_id, option
  41. end
  42. end
  43. 1 def all_follow_rule_options follower_id
  44. 365 follow_rule = preference :follow, follower_id
  45. 365 return [] unless follow_rule.present?
  46. 365 follow_rule.split("\n")
  47. end
  48. 1 def follow_rule_option_applies? follower_id, option
  49. 700 option_code = option.to_name.code
  50. 700 candidate_ids = follower_candidate_ids_for_option option_code
  51. 700 follow_rule_option_applies_to_candidates? follower_id, option_code, candidate_ids
  52. end
  53. 1 def follow_rule_option_applies_to_candidates? follower_id, option_code, candidate_ids
  54. 700 if (test = FollowOption.test[option_code])
  55. 30 test.call follower_id, candidate_ids
  56. else
  57. 670 candidate_ids.include? follower_id
  58. end
  59. end
  60. 1 def follower_candidate_ids_for_option option_code
  61. 700 return [] unless (block = FollowOption.follower_candidate_ids[option_code])
  62. 670 block.call self
  63. end
  64. end;end;end;end;end;
  65. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/followed_by.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/follow/follower_ids.rb

92.75% lines covered

69 relevant lines. 64 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Follow;
  3. # Set: All cards (Follow, FollowerIds)
  4. #
  5. 1 module FollowerIds;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follower_ids.rb"; end
  8. 1 FOLLOWER_IDS_CACHE_KEY = "FOLLOWER_IDS".freeze
  9. 1 card_accessor :followers
  10. 1 event :cache_expired_for_type_change, :store, on: :update, changed: %i[type_id name] do
  11. 1 act_card&.schedule_preference_expiration
  12. # FIXME: expire (also?) after save
  13. 1 Card.follow_caches_expired
  14. end
  15. 1 def schedule_preference_expiration
  16. 1 @expire_preferences_scheduled = true
  17. end
  18. 1 def expire_preferences?
  19. 216 @expire_preferences_scheduled
  20. end
  21. 1 event :expire_preferences_cache, :finalize, when: :expire_preferences? do
  22. 1 Card::Rule.clear_preference_cache
  23. end
  24. # follow cache methods on Card class
  25. 1 module ClassMethods
  26. 1 def follow_caches_expired
  27. 42 Card.clear_follower_ids_cache
  28. 42 Card::Rule.clear_preference_cache
  29. end
  30. 1 def follower_ids_cache
  31. 12 Card.cache.read(FOLLOWER_IDS_CACHE_KEY) || {}
  32. end
  33. 1 def write_follower_ids_cache hash
  34. 4 Card.cache.write FOLLOWER_IDS_CACHE_KEY, hash
  35. end
  36. 1 def clear_follower_ids_cache
  37. 42 Card.cache.write FOLLOWER_IDS_CACHE_KEY, nil
  38. end
  39. end
  40. 1 def write_follower_ids_cache user_ids
  41. 4 hash = Card.follower_ids_cache
  42. 4 hash[id] = user_ids
  43. 4 Card.write_follower_ids_cache hash
  44. end
  45. 1 def read_follower_ids_cache
  46. 8 Card.follower_ids_cache[id]
  47. end
  48. 1 def follower_names
  49. 8 followers.map(&:name)
  50. end
  51. 1 def followers
  52. 8 follower_ids.map do |id|
  53. 16 Card.fetch(id)
  54. end
  55. end
  56. 1 def follower_ids
  57. 8 @follower_ids = read_follower_ids_cache || begin
  58. 4 result = direct_follower_ids + indirect_follower_ids
  59. 4 write_follower_ids_cache result
  60. 4 result
  61. end
  62. end
  63. 1 def followers_count
  64. follower_ids.size
  65. end
  66. 1 def indirect_follower_ids
  67. 4 result = ::Set.new
  68. 4 left_card = left
  69. 4 while left_card
  70. result += left_card.direct_follower_ids if left_card.followed_field? self
  71. left_card = left_card.left
  72. end
  73. 4 result
  74. end
  75. # all users (cards) that "directly" follow this card
  76. # "direct" means there is a follow rule that applies explicitly to this card.
  77. # one can also "indirectly" follow cards by following parent cards or other
  78. # cards that nest this one.
  79. 1 def direct_followers
  80. direct_follower_ids.map do |id|
  81. Card.fetch(id)
  82. end
  83. end
  84. 1 def direct_follower_ids &block
  85. 169 ids = ::Set.new
  86. 169 set_names.each do |set_name|
  87. 954 direct_follower_ids_for_set setcard_from_name(set_name), ids, &block
  88. end
  89. 169 ids
  90. end
  91. 1 def setcard_from_name set_name
  92. 954 Card.fetch set_name, new: { type_id: SetID }
  93. end
  94. 1 def direct_follower_ids_for_set set_card, ids
  95. 954 set_card.all_user_ids_with_rule_for(:follow).each do |user_id|
  96. 368 next if ids.include?(user_id) || !(option = follow_rule_option user_id)
  97. 28 yield user_id, set_card, option if block_given?
  98. 28 ids << user_id
  99. end
  100. end
  101. 1 def each_direct_follower_id_with_reason
  102. 165 direct_follower_ids do |user_id, set_card, follow_option|
  103. 20 reason = follow_option.gsub(/[\[\]]/, "")
  104. 20 yield user_id, set_card: set_card, option: reason
  105. end
  106. end
  107. end;end;end;end;end;
  108. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/follower_ids.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/follow/start_follow_link.rb

50.0% lines covered

10 relevant lines. 5 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Follow)
  4. #
  5. #! no set module
  6. 1 module Follow;
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/start_follow_link.rb"; end
  8. 1 class StartFollowLink < FollowLink
  9. 1 def initialize format
  10. @rule_content = "*always"
  11. @link_text = "follow"
  12. @action = "send"
  13. @css_class = ""
  14. super
  15. end
  16. end
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/start_follow_link.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/follow/stop_follow_link.rb

45.45% lines covered

11 relevant lines. 5 lines covered and 6 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Follow)
  4. #
  5. #! no set module
  6. 1 module Follow;
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/stop_follow_link.rb"; end
  8. 1 class StopFollowLink < FollowLink
  9. 1 def initialize format
  10. @rule_content = "*never"
  11. @link_text = "following"
  12. @hover_text = "unfollow"
  13. @action = "stop sending"
  14. @css_class = "btn-item-delete"
  15. super
  16. end
  17. end
  18. end;end;end;end;
  19. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/follow/stop_follow_link.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/notify.rb

95.45% lines covered

44 relevant lines. 42 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Notify)
  4. #
  5. 1 module Notify;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify.rb"; end
  8. 1 attr_accessor :follower_stash
  9. 1 mattr_accessor :force_notifications
  10. 1 event :silence_notifications, :initialize, when: :silence_notifications? do
  11. @silent_change = true
  12. end
  13. 1 def silence_notifications?
  14. 475 !(Card::Env[:controller] || force_notifications)
  15. end
  16. 1 event :notify_followers_after_save,
  17. :integrate_with_delay, on: :save, when: :notable_change? do
  18. 85 notify_followers
  19. end
  20. # in the delete case we have to calculate the follower_stash beforehand
  21. # but we can't pass the follower_stash through the ActiveJob queue.
  22. # We have to deal with the notifications in the integrate phase instead of the
  23. # integrate_with_delay phase
  24. 1 event :stash_followers, :store, on: :delete, when: :notable_change? do
  25. 1 act_card.follower_stash ||= FollowerStash.new
  26. 1 act_card.follower_stash.check_card self
  27. end
  28. 1 event :notify_followers_after_delete, :integrate, on: :delete, when: :notable_change? do
  29. 1 notify_followers
  30. end
  31. 1 def notify_followers
  32. 86 return unless (act = Card::Director.act)
  33. 86 act.reload
  34. 86 notify_followers_of act
  35. end
  36. 1 def notable_change?
  37. 222 !silent_change? && current_act_card? &&
  38. 89 (Card::Auth.current_id != WagnBotID) && followable?
  39. end
  40. 1 def silent_change?
  41. 376 silent_change
  42. end
  43. 1 private
  44. 1 def notify_followers_of act
  45. 86 act_followers(act).each_follower_with_reason do |follower, reason|
  46. 18 next if !follower.account || (follower == act.actor)
  47. 17 notify_follower follower, act, reason
  48. end
  49. end
  50. 1 def notify_follower follower, act, reason
  51. 17 follower.account.send_change_notice act, reason[:set_card].name, reason[:option]
  52. end
  53. 1 def act_followers act
  54. 86 @follower_stash ||= FollowerStash.new
  55. 86 act.actions(false).each do |a|
  56. 155 next if !a.card || a.card.silent_change?
  57. 154 @follower_stash.check_card a.card
  58. end
  59. 86 @follower_stash
  60. end
  61. 1 def silent_change
  62. @silent_change || @supercard&.silent_change
  63. end
  64. 1 def current_act_card?
  65. 222 return false unless act_card
  66. 222 act_card.id.nil? || act_card.id == id
  67. # FIXME: currently card_id is nil for deleted acts (at least
  68. # in the store phase when it's tested). The nil test was needed
  69. # to make this work.
  70. end
  71. end;end;end;end;
  72. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/notify/base_views.rb

94.37% lines covered

71 relevant lines. 67 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Notify;
  3. # Set: All cards (Notify, BaseViews)
  4. #
  5. 1 module BaseViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify/base_views.rb"; end
  8. 2 module Format; module_parent.send :register_set_format, Card::Format, self; extend Card::Set::AbstractFormat
  9. 1 view :list_of_changes, denial: :blank, cache: :never do
  10. 48 action = notification_action voo.action_id
  11. 48 relevant_fields(action).map do |type|
  12. 118 edit_info_for(type, action)
  13. end.compact.join
  14. end
  15. 1 view :subedits, perms: :none, cache: :never do
  16. 34 return unless notification_act
  17. 34 wrap_subedits do
  18. 34 notification_act.actions_affecting(card).map do |action|
  19. 42 next if action.card_id == card.id
  20. 14 action.card.format(format: @format).render_subedit_notice action_id: action.id
  21. end
  22. end
  23. end
  24. 1 view :subedit_notice, cache: :never do
  25. 14 action = notification_action voo.action_id
  26. 14 wrap_subedit_item do
  27. 14 %(#{name_before_action action} #{action.action_type}d\n) +
  28. render_list_of_changes(action_id: action.id)
  29. end
  30. end
  31. 1 view :followed, perms: :none, compact: true do
  32. 34 if (set_card = followed_set_card) && (option_card = follow_option_card)
  33. 34 option_card.description set_card
  34. else
  35. "*followed set of cards*"
  36. end
  37. end
  38. 1 view :follower, perms: :none, compact: true do
  39. active_notice(:follower) || "follower"
  40. end
  41. 1 view :last_action_verb, cache: :never do
  42. 51 "#{notification_act&.main_action&.action_type || 'edite'}d"
  43. end
  44. 1 view :unfollow_url, perms: :none, compact: true, cache: :never do
  45. 34 return "" unless (rule_name = live_follow_rule_name)
  46. 34 card_url path(mark: "#{active_notice(:follower)}+#{:follow.cardname}",
  47. action: :update,
  48. card: { subcards: { rule_name => Card[:never].name } })
  49. end
  50. 1 def relevant_fields action
  51. 48 case action.action_type
  52. 22 when :create then %i[cardtype content]
  53. 24 when :update then %i[name cardtype content]
  54. 2 when :delete then %i[content]
  55. end
  56. end
  57. 1 def name_before_action action
  58. 14 (action.value(:name) && action.previous_value(:name)) || card.name
  59. end
  60. 1 def followed_set_card
  61. 68 (set_name = active_notice(:followed_set)) && Card.fetch(set_name)
  62. end
  63. 1 def follow_option_card
  64. 34 return unless (option_name = active_notice(:follow_option))
  65. 34 Card.fetch option_name
  66. end
  67. 1 def active_notice key
  68. 170 @active_notice ||= inherit :active_notice
  69. 170 return unless @active_notice
  70. 170 @active_notice[key]
  71. end
  72. 1 def live_follow_rule_name
  73. 34 return unless (set_card = followed_set_card) && (follower = active_notice(:follower))
  74. 34 set_card.follow_rule_name follower
  75. end
  76. 1 def edit_info_for field, action
  77. 118 return nil unless (value = action.value field)
  78. 68 value = action.previous_value if action.action_type == :delete
  79. 68 wrap_list_item " #{notification_action_label action} #{field}: #{value}"
  80. end
  81. 1 def notification_action_label action
  82. 68 case action.action_type
  83. 24 when :update then "new"
  84. when :delete then "deleted"
  85. end
  86. end
  87. 1 def wrap_subedits
  88. 34 subedits = yield.compact.join
  89. 34 return "" if subedits.blank?
  90. 8 "\nThis update included the following changes:\n#{wrap_list subedits}"
  91. end
  92. 1 def wrap_list list
  93. 4 "\n#{list}\n"
  94. end
  95. 1 def wrap_list_item item
  96. 25 "#{item}\n"
  97. end
  98. 1 def wrap_subedit_item
  99. "\n#{yield}\n"
  100. end
  101. 1 def notification_act act=nil
  102. 119 @notification_act ||= act || card.acts.last
  103. end
  104. 1 def notification_action action_id
  105. 62 action_id ? Action.fetch(action_id) : card.last_action
  106. end
  107. end
  108. end;end;end;end;end;
  109. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify/base_views.rb ~~

card/tmpsets/set/mod022-card-mod-follow/all/notify/html_views.rb

100.0% lines covered

11 relevant lines. 11 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module Notify;
  3. # Set: All cards (Notify, HtmlViews)
  4. #
  5. 1 module HtmlViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify/html_views.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. # view :last_action, perms: :none, cache: :never do
  10. # _render_last_action_verb
  11. # end
  12. 1 def wrap_list list
  13. 4 "<ul>#{list}</ul>\n"
  14. end
  15. 1 def wrap_list_item item
  16. 43 "<li>#{item}</li>\n"
  17. end
  18. 1 def wrap_subedit_item
  19. 14 "<li>#{yield}</li>\n"
  20. end
  21. end
  22. end;end;end;end;end;
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/all/notify/html_views.rb ~~

card/tmpsets/set/mod022-card-mod-follow/right/account.rb

100.0% lines covered

11 relevant lines. 11 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Account" cards
  4. #
  5. 1 module Account;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/account.rb"; end
  8. 1 def send_change_notice act, followed_set, follow_option
  9. 17 return unless email.present? && changes_visible?(act)
  10. 17 notify_of_act act do
  11. 17 { follower: left.name, followed_set: followed_set, follow_option: follow_option }
  12. end
  13. end
  14. 1 def notify_of_act act
  15. 17 Auth.as(left.id) do
  16. 17 Card[:follower_notification_email].deliver(
  17. act.card, { to: email }, auth: left, active_notice: yield
  18. )
  19. end
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/account.rb ~~

card/tmpsets/set/mod022-card-mod-follow/right/follow.rb

55.0% lines covered

60 relevant lines. 33 lines covered and 27 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Follow" cards
  4. #
  5. # The Right::Follow set configures follow preferences (`[Set]+[User]+:follow`)
  6. # While the user follow dashboard ([User]+:follow`) is also in this Set, its
  7. 1 module Follow;
  8. 1 extend Card::Set
  9. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/follow.rb"; end
  10. # customizations are handled in TypePlusRight::User::Follow
  11. 1 event :cache_expired_for_new_preference, :integrate, when: :is_preference? do
  12. 21 Card.follow_caches_expired
  13. end
  14. 1 def option_cards
  15. Card::FollowOption.cards.compact
  16. end
  17. 1 def options_rule_card
  18. Card.new(
  19. name: "follow_options_card",
  20. type_code: :pointer,
  21. content: option_cards.map { |oc| "[[#{oc.name}]]" }.join("\n")
  22. )
  23. end
  24. 1 def add_follow_item? condition
  25. new_card? || !include_item?(condition)
  26. end
  27. 1 def ok_to_update
  28. 4 permit :update
  29. end
  30. 1 def ok_to_create
  31. 20 permit :create
  32. end
  33. 1 def ok_to_delete
  34. 1 permit :delete
  35. end
  36. 1 def permit action, verb=nil
  37. 25 if %i[create delete update].include?(action) && allowed_to_change_follow_status?
  38. 25 true
  39. else
  40. super action, verb
  41. end
  42. end
  43. 1 def allowed_to_change_follow_status?
  44. 25 Auth.signed_in? &&
  45. ((user = rule_user) && Auth.current_id == user.id) || Auth.always_ok?
  46. end
  47. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  48. # shows a follow item link for each of the current follow options
  49. 1 view :follow_status, cache: :never do
  50. wrap { haml :follow_status }
  51. end
  52. # interface to view/alter a specific rule option
  53. 1 view :follow_item, cache: :never do
  54. follow_item Env.params[:condition]
  55. end
  56. 1 def follow_item condition, button=true
  57. condition ||= "*always"
  58. wrap do
  59. card_form action: :update, success: { view: :follow_item } do
  60. [
  61. follow_item_hidden_tags(condition),
  62. (follow_item_button(condition) if button),
  63. follow_item_link(condition)
  64. ].compact
  65. end
  66. end
  67. end
  68. 1 def rule_form_args
  69. super.merge "data-update-foreign-slot": ".card-slot.follow_section-view"
  70. end
  71. 1 private
  72. 1 def follow_item_hidden_tags condition
  73. condkey = card.add_follow_item?(condition) ? :add_item : :drop_item
  74. hidden_tags condition: condition, condkey => condition
  75. end
  76. 1 def follow_item_button condition
  77. action = card.add_follow_item?(condition) ? :add : :delete
  78. button_tag type: :submit, "aria-label": "Left Align",
  79. class: "btn-sm btn-item #{follow_item_button_class action}" do
  80. follow_item_icon action
  81. end
  82. end
  83. 1 def follow_item_button_class action
  84. action == :add ? "btn-item-add" : "btn-item-delete btn-primary"
  85. end
  86. 1 def follow_item_icon action
  87. icon_tag(action == :add ? :add : :check)
  88. end
  89. 1 def follow_item_link condition
  90. link_to_card follow_item_link_target, follow_item_link_text(condition)
  91. end
  92. 1 def follow_item_link_target
  93. set = card.rule_set
  94. setname = set.name
  95. set.tag.codename == :self ? setname.left : setname.field("by name")
  96. end
  97. 1 def follow_item_link_text condition
  98. if (option_card = Card.fetch condition)
  99. option_card.description card.rule_set
  100. else
  101. card.rule_set.follow_label
  102. end
  103. end
  104. end
  105. end;end;end;end;
  106. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/follow.rb ~~

card/tmpsets/set/mod022-card-mod-follow/right/follow_fields.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+FollowFields" cards
  4. #
  5. 1 module FollowFields;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/follow_fields.rb"; end
  8. 1 event :follow_fields_changed, :integrate do
  9. Card.follow_caches_expired
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/follow_fields.rb ~~

card/tmpsets/set/mod022-card-mod-follow/right/followers.rb

92.86% lines covered

14 relevant lines. 13 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Followers" cards
  4. #
  5. # -*- encoding : utf-8 -*-
  6. 1 module Followers;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/followers.rb"; end
  9. # X+*followers provides a list of all users following X.
  10. 1 include_set Abstract::Pointer
  11. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  12. 1 view :core, cache: :never do
  13. 4 super()
  14. end
  15. end
  16. 1 def content
  17. left ? item_names.to_pointer_content : ""
  18. end
  19. 1 def item_names _args={}
  20. 8 left ? left.follow_set_card.prototype.follower_names : []
  21. end
  22. 1 def virtual?
  23. 24 new?
  24. end
  25. end;end;end;end;
  26. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/followers.rb ~~

card/tmpsets/set/mod022-card-mod-follow/right/following.rb

43.33% lines covered

30 relevant lines. 13 lines covered and 17 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Following" cards
  4. #
  5. 1 module Following;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/right/following.rb"; end
  8. 1 def virtual?
  9. new?
  10. end
  11. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  12. 1 view :core do
  13. if card.left && Auth.signed_in?
  14. render_rule_editor
  15. else
  16. nest Card.fetch(card.name.left, :followers), view: :titled, items: { view: :link }
  17. end
  18. end
  19. 1 view :status do
  20. if (rcard = current_follow_rule_card)
  21. rcard.item_cards.map do |item|
  22. %(<div class="alert alert-success" role="alert">
  23. <strong>#{rcard.rule_set.follow_label}</strong>: #{item.title}
  24. </div>)
  25. end.join
  26. else
  27. "No following preference"
  28. end
  29. end
  30. 1 view :one_line_content do
  31. ""
  32. end
  33. 1 view :rule_editor, cache: :never do
  34. rule_context = Card.fetch preference_name, new: { type_id: PointerID }
  35. wrap_with :div, class: "edit-rule" do
  36. follow_context = current_follow_rule_card || rule_context
  37. subformat(follow_context).rule_form :open, rule_context, :modal
  38. end
  39. end
  40. 1 def preference_name
  41. set_name = card.left.follow_set_card.name
  42. Card::Name[set_name, Auth.current.name, :follow]
  43. end
  44. 1 def edit_rule_success
  45. { view: "status", id: card.name.url_key }
  46. end
  47. 1 def current_follow_rule_card
  48. card.left.preference :follow
  49. end
  50. end
  51. end;end;end;end;
  52. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/right/following.rb ~~

card/tmpsets/set/mod022-card-mod-follow/self/always.rb

81.82% lines covered

11 relevant lines. 9 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Always"
  4. #
  5. 1 module Always;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/always.rb"; end
  8. 1 include_set Abstract::FollowOption
  9. 1 follow_opts position: 2
  10. 29 follow_test { |_follower_id, _accounted_ids| true }
  11. 1 def title
  12. "Following"
  13. end
  14. 1 def label
  15. "follow"
  16. end
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/always.rb ~~

card/tmpsets/set/mod022-card-mod-follow/self/created.rb

78.57% lines covered

14 relevant lines. 11 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Created"
  4. #
  5. 1 module Created;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/created.rb"; end
  8. 1 include_set Abstract::FollowOption
  9. 1 restrictive_follow_opts position: 1
  10. 1 follower_candidate_ids do |card|
  11. 335 [card.creator_id]
  12. end
  13. 1 def title
  14. "Following content you created"
  15. end
  16. 1 def label
  17. "follow if I created"
  18. end
  19. 1 def description set_card
  20. "#{set_card.follow_label} I created"
  21. end
  22. end;end;end;end;
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/created.rb ~~

card/tmpsets/set/mod022-card-mod-follow/self/edited.rb

78.57% lines covered

14 relevant lines. 11 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Edited"
  4. #
  5. 1 module Edited;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/edited.rb"; end
  8. 1 include_set Abstract::FollowOption
  9. 1 restrictive_follow_opts position: 2
  10. 1 follower_candidate_ids do |card|
  11. # FIXME? - could optimize by not using cards table...
  12. 335 card.id ? Card.search(editor_of: card.id, return: :id) : []
  13. end
  14. 1 def title
  15. "Following content you edited"
  16. end
  17. 1 def label
  18. "follow if I edited"
  19. end
  20. 1 def description set_card
  21. "#{set_card.follow_label} I edited"
  22. end
  23. end;end;end;end;
  24. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/edited.rb ~~

card/tmpsets/set/mod022-card-mod-follow/self/follow.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Follow"
  4. #
  5. 1 module Follow;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/follow.rb"; end
  8. 1 extend Card::Setting
  9. 1 setting_opts group: :other, position: 7, rule_type_editable: false, user_specific: true
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/follow.rb ~~

card/tmpsets/set/mod022-card-mod-follow/self/follow_defaults.rb

35.9% lines covered

39 relevant lines. 14 lines covered and 25 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "FollowDefaults"
  4. #
  5. # DEPRECATED
  6. #
  7. # Despite its name (*follow defaults)card does not influence defaults for *follow rules.
  8. # What it does is provide a mechanism (with interface) for updating all users so that
  9. # they follow the items that are its content.
  10. #
  11. # PLAN:
  12. 1 module FollowDefaults;
  13. 1 extend Card::Set
  14. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/follow_defaults.rb"; end
  15. # - actual defaults should be handled as much as possible with something like
  16. # the *defaults rule
  17. # - on the *admin page, we can have a link so sharks can update all the pristine cards
  18. # to use whatever the actual defaults representation is (see previous point)
  19. # - if you truly want to override existing follow rules, that may be monkey territory?
  20. # - we will delete "*follow defaults" after the above are completed
  21. 1 event :update_follow_rules, :finalize, on: :save, when: :update_all_users do
  22. Auth.as_bot do
  23. Card.search(type: "user").each do |user|
  24. follow_defaults.each do |set_card, option|
  25. follow_rule = Card.fetch(set_card.follow_rule_name(user.name), new: {})
  26. next unless follow_rule
  27. follow_rule.drop_item "*never"
  28. follow_rule.drop_item "*always"
  29. follow_rule.add_item option
  30. follow_rule.save!
  31. end
  32. end
  33. end
  34. Card.follow_caches_expired
  35. end
  36. 1 def follow_defaults
  37. item_names.map do |item|
  38. if (set_card = Card.fetch item.to_name.left)&.type_code == :set
  39. [set_card, follow_option(item)]
  40. end
  41. end.compact
  42. end
  43. 1 def follow_option item
  44. option_card =
  45. Card.fetch(item.to_name.right) || Card[item.to_name.right.to_sym]
  46. option_card.follow_option? ? option_card.name : "*always"
  47. end
  48. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  49. 1 view :edit, perms: :update, unknown: true do
  50. frame_and_form :update, hidden: { success: "_self",
  51. card: { update_all_users: false } } do
  52. [
  53. _render_content_formgroups,
  54. _render_confirm_update_all,
  55. _render_edit_buttons
  56. ]
  57. end
  58. end
  59. 1 view :edit_buttons do
  60. button_formgroup do
  61. [submit_and_update_button, simple_submit_button, cancel_to_edit_button]
  62. end
  63. end
  64. 1 def submit_and_update_button
  65. submit_button text: "Submit and update all users",
  66. disable_with: "Updating", class: "follow-updater"
  67. end
  68. 1 def simple_submit_button
  69. button_tag "Submit", class: "follow"
  70. end
  71. 1 def cancel_to_edit_button
  72. cancel_button href: path(view: :edit, id: card.id)
  73. end
  74. 1 view :confirm_update_all do
  75. wrap do
  76. alert "info" do
  77. %(
  78. <h1>Are you sure you want to change the default follow rules?</h1>
  79. <p>You may choose to update all existing users.
  80. This may take a while. </p>
  81. )
  82. end
  83. end
  84. end
  85. end
  86. end;end;end;end;
  87. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/follow_defaults.rb ~~

card/tmpsets/set/mod022-card-mod-follow/self/never.rb

81.82% lines covered

11 relevant lines. 9 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "Never"
  4. #
  5. 1 module Never;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/self/never.rb"; end
  8. 1 include_set Abstract::FollowOption
  9. 1 follow_opts position: 3
  10. 3 follow_test { |_follower_id, _accounted_ids| false }
  11. 1 def title
  12. "Ignoring"
  13. end
  14. 1 def label
  15. "ignore"
  16. end
  17. end;end;end;end;
  18. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/self/never.rb ~~

card/tmpsets/set/mod022-card-mod-follow/type/cardtype.rb

66.67% lines covered

15 relevant lines. 10 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Cardtype" cards
  4. #
  5. 1 module Cardtype;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type/cardtype.rb"; end
  8. 1 def follow_label
  9. follow_set_card.follow_label
  10. end
  11. 1 def followed_by? user_id=nil
  12. follow_set_card.all_members_followed_by? user_id
  13. end
  14. 1 def follow_set_card
  15. Card.fetch name, :type
  16. end
  17. 1 def list_direct_followers?
  18. true
  19. end
  20. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  21. 1 def related_by_type_items
  22. super.unshift ["#{card.name} cards", [card, :type, :by_name], mark: :absolute]
  23. end
  24. end
  25. end;end;end;end;
  26. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type/cardtype.rb ~~

card/tmpsets/set/mod022-card-mod-follow/type/set.rb

64.52% lines covered

31 relevant lines. 20 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Set" cards
  4. #
  5. 1 module Set;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type/set.rb"; end
  8. 1 event :cache_expired_for_new_set, :store, on: :create do
  9. 20 Card.follow_caches_expired
  10. end
  11. 1 def list_direct_followers?
  12. true
  13. end
  14. 1 def follow_label
  15. 36 if (klass = subclass_for_set)
  16. 36 klass.short_label name.left_name
  17. else
  18. ""
  19. end
  20. end
  21. 1 def follow_rule_name user=nil
  22. 34 Card::Name[[name, user, :follow].compact]
  23. end
  24. 1 def followed_by? user_id=nil
  25. all_members_followed_by? user_id
  26. end
  27. 1 def follow_set_card
  28. 4 self
  29. end
  30. 1 def all_members_followed?
  31. all_members_followed_by? Auth.current_id
  32. end
  33. 1 def all_members_followed_by? user_id=nil
  34. return false unless prototype.followed_by?(user_id)
  35. directly_followed_by?(user_id) || broader_set_followed_by?(user_id)
  36. end
  37. 1 def broader_set_followed_by? user_id
  38. broader_sets.find do |set_name|
  39. Card.fetch(set_name)&.directly_followed_by? user_id
  40. end
  41. end
  42. 1 def directly_followed?
  43. directly_followed_by? Auth.current_id
  44. end
  45. 1 def directly_followed_by? user_id=nil
  46. return true if user_id && follow_rule?(user_id)
  47. follow_rule?
  48. end
  49. end;end;end;end;
  50. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type/set.rb ~~

card/tmpsets/set/mod022-card-mod-follow/type/user.rb

55.56% lines covered

9 relevant lines. 5 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "User" cards
  4. #
  5. 1 module User;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type/user.rb"; end
  8. 1 def follow follow_name, option="*always"
  9. return unless (follow_rule = Card.fetch(follow_name)&.follow_rule_card(name, new: {}))
  10. follow_rule.drop_item "*never"
  11. follow_rule.add_item option
  12. follow_rule.save!
  13. end
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type/user.rb ~~

card/tmpsets/set/mod022-card-mod-follow/type_plus_right/user/follow.rb

52.38% lines covered

42 relevant lines. 22 lines covered and 20 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class TypePlusRight; module User;
  3. # Set: All "+Follow" cards on "User" cards
  4. #
  5. 1 module Follow;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type_plus_right/user/follow.rb"; end
  8. 1 FOLLOW_TABS = { "Follow" => "follow_tab", "Ignore" => "ignore_tab" }.freeze
  9. # a virtual pointer to the sets that a user is following.
  10. # (data is stored in preferences: `[Set]+[User]+:follow`)
  11. 1 include_set Abstract::Pointer
  12. 1 def virtual?
  13. new?
  14. end
  15. # overrides pointer default
  16. 1 def item_names _args={}
  17. 4 if (user = left)
  18. 4 Card::Rule.preference_names user.name, "follow"
  19. else
  20. []
  21. end
  22. end
  23. 1 def suggestions
  24. Card[:follow_suggestions]&.item_names || []
  25. end
  26. 1 def current_user?
  27. Auth.signed_in? && Auth.current_id == left.id
  28. end
  29. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  30. 1 view :one_line_content do
  31. ""
  32. end
  33. 1 view :edit do
  34. render :open
  35. end
  36. # renders follow tab and ignore tab
  37. 1 view :core do
  38. 2 tabs FOLLOW_TABS, "follow_tab", load: :lazy do
  39. render_follow_tab
  40. end
  41. end
  42. 1 view :follow_tab, cache: :never do
  43. haml :follow_editor, items_method: :following_rules_and_options
  44. end
  45. 1 view :ignore_tab, cache: :never do
  46. haml :follow_editor, items_method: :ignoring_rules_and_options
  47. end
  48. 1 def show_button?
  49. card.current_user? || Auth.always_ok?
  50. end
  51. 1 def pointer_items args
  52. voo.items[:view] ||= :link
  53. super(args)
  54. end
  55. # TODO: research and generalize
  56. # this does not look specific to following!
  57. 1 view :errors, perms: :none do
  58. return unless card.errors.any?
  59. if card.errors.find { |attrib, _msg| attrib == :permission_denied }
  60. Env.save_interrupted_action(request.env["REQUEST_URI"])
  61. voo.title = "Problems with #{card.name}"
  62. class_up "d0-card-frame", "card card-warning card-inverse"
  63. frame do
  64. "Please #{link_to_card :signin, 'sign in'}" # " #{to_task}"
  65. end
  66. else
  67. super()
  68. end
  69. end
  70. end
  71. end;end;end;end;end;
  72. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type_plus_right/user/follow.rb ~~

card/tmpsets/set/mod022-card-mod-follow/type_plus_right/user/follow/follow_editor_helper.rb

31.67% lines covered

60 relevant lines. 19 lines covered and 41 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 5 class Card; module Set; class TypePlusRight; module User;; module Follow;
  3. # Set: All "+FollowEditorHelper" cards on "User+Follow" cards (FollowEditorHelper)
  4. #
  5. # all the following methods are used to construct the Follow and Ignore tabs
  6. 1 module FollowEditorHelper;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-follow/set/type_plus_right/user/follow/follow_editor_helper.rb"; end
  9. # TODO: these object representations are complex enough for their own class
  10. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  11. # constructs hash of rules/options for "Follow" tab
  12. 1 def following_rules_and_options &block
  13. rule_opt_array = following_rule_options_hash.map do |key, val|
  14. [(Card.fetch key, new: {}), val]
  15. end
  16. rules_and_options_by_set_pattern Hash[rule_opt_array], &block
  17. end
  18. # constructs hash of rules/options for "Ignore" tab
  19. 1 def ignoring_rules_and_options &block
  20. rule_opts_hash = ignore_rules.each_with_object({}) do |rule, hash|
  21. hash[rule] = [:never.cardname]
  22. end
  23. rules_and_options_by_set_pattern rule_opts_hash, &block
  24. end
  25. 1 private
  26. # all rules with ignore
  27. 1 def ignore_rules
  28. never = :never.cardname.key
  29. card.item_cards.select do |follow_rule|
  30. follow_rule.item_names.select { |n| n.key == never }.any?
  31. end
  32. end
  33. # @param rule_opts_hash [Hash] { rule1_card => rule1_follow_options }
  34. # for each rule/option variant, yields with rule_card and option params
  35. 1 def rules_and_options_by_set_pattern rule_opts_hash
  36. pattern_hash = a_set_pattern_hash rule_opts_hash
  37. empty = true
  38. Card.set_patterns.reverse.map do |pattern|
  39. pattern_hash[pattern].each do |rule_card, options|
  40. options.each do |option|
  41. yield rule_card, option
  42. empty = false
  43. end
  44. end
  45. end
  46. yield nil if empty
  47. end
  48. 1 def a_set_pattern_hash rule_opts_hash
  49. pattern_hash = Hash.new { |h, k| h[k] = [] }
  50. rule_opts_hash.each do |rule_card, options|
  51. pattern_hash[rule_card.rule_set.subclass_for_set] << [rule_card, options]
  52. end
  53. pattern_hash
  54. end
  55. # @return Hash # { rule1 => rule1_follow_options }
  56. 1 def following_rule_options_hash
  57. merge_option_hashes current_following_rule_options_hash,
  58. suggested_following_rule_options_hash
  59. end
  60. # adds suggested follow options to existing rules where applicable
  61. 1 def merge_option_hashes current, suggested
  62. current.each do |key, current_opt|
  63. if (suggested_opt = suggested.delete(key))
  64. current[key] = (current_opt + suggested_opt).uniq
  65. end
  66. end
  67. current.merge suggested
  68. end
  69. # @return Hash # { existing_rule1 => rule1_follow_options } (excluding never)
  70. # (*never is excluded because this list is for the Follow tab, and *never is
  71. # handled under the Ignore tab)
  72. 1 def current_following_rule_options_hash
  73. never = :never.cardname
  74. card.item_cards.each_with_object({}) do |follow_rule, hash|
  75. hash[follow_rule.key] = follow_rule.item_names.reject { |item| item == never }
  76. end
  77. end
  78. # @return Hash # { suggested_rule1 => rule1_follow_options }
  79. 1 def suggested_following_rule_options_hash
  80. return {} unless card.current_user?
  81. card.suggestions.each_with_object({}) do |sug, hash|
  82. set_card, opt = a_set_and_option_suggestion(sug) || a_set_only_suggestion(sug)
  83. hash[set_card.follow_rule_name(card.trunk).key] = [opt]
  84. end
  85. end
  86. # @param sug [String] follow suggestion
  87. # @return [Array] set_card and option
  88. # suggestion value contains both set and follow option
  89. 1 def a_set_and_option_suggestion sug
  90. return unless (set_card = valid_set_card(sug.to_name.left))
  91. [set_card, suggested_follow_option(sug.to_name.right)]
  92. end
  93. 1 def suggested_follow_option name
  94. # FIXME: option should be unambiguously name or codename
  95. # (if codename use colon or Symbol)
  96. option_card = Card.fetch(name) || Card[name.to_sym]
  97. option_card&.follow_option? ? option_card.name : :always.cardname
  98. end
  99. # @param sug [String] follow suggestion
  100. # @return [Array] set_card and option
  101. # suggestion value contains only set (implies *always)
  102. 1 def a_set_only_suggestion sug
  103. return unless (set_card = valid_set_card(sug))
  104. yield set_card, :always.cardname
  105. end
  106. 1 def valid_set_card name
  107. card = Card.fetch(name)
  108. card&.type_code == :set ? card : false
  109. end
  110. end
  111. end;end;end;end;end;end;
  112. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-follow/set/type_plus_right/user/follow/follow_editor_helper.rb ~~

card/tmpsets/set/mod023-card-mod-google_analytics/all/google_analytics.rb

83.33% lines covered

24 relevant lines. 20 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (GoogleAnalytics)
  4. #
  5. 1 module GoogleAnalytics;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-google_analytics/set/all/google_analytics.rb"; end
  8. 1 require "staccato"
  9. 1 mattr_accessor :server_side_tracking_formats
  10. 1 self.server_side_tracking_formats = %i[csv json]
  11. 1 event :track_page, before: :show_page, when: :track_page? do
  12. tracker.pageview path: Env.controller.request&.path, host: Env.host, title: name
  13. end
  14. 1 def track_page?
  15. 315 google_analytics_key &&
  16. Env.controller&.response_format&.in?(server_side_tracking_formats)
  17. end
  18. 1 def tracker
  19. return unless google_analytics_key
  20. ::Staccato.tracker google_analytics_key # , nil, ssl: true
  21. end
  22. 1 def google_analytics_key
  23. 539 @google_analytics_key ||=
  24. Card::Rule.global_setting(:google_analytics_key) ||
  25. Card.config.google_analytics_key
  26. end
  27. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  28. 1 delegate :tracker, :google_analytics_key, to: :card
  29. 1 def views_in_head
  30. 224 super << :google_analytics_snippet
  31. end
  32. 1 view :google_analytics_snippet, unknown: true, perms: :none do
  33. 224 haml :google_analytics_snippet if google_analytics_key
  34. end
  35. 1 def google_analytics_snippet_vars
  36. { anonymizeIp: true }
  37. end
  38. end
  39. end;end;end;end;
  40. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-google_analytics/set/all/google_analytics.rb ~~

card/tmpsets/set/mod024-card-mod-markdown/type/markdown.rb

84.62% lines covered

13 relevant lines. 11 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Markdown" cards
  4. #
  5. 1 module Markdown;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-markdown/set/type/markdown.rb"; end
  8. 1 require "kramdown"
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 view :core do
  11. 50 safe_process_content do |content|
  12. 50 Kramdown::Document.new(content).to_html
  13. end
  14. end
  15. 1 def input_type
  16. :ace_editor
  17. end
  18. 1 def ace_mode
  19. :markdown
  20. end
  21. end
  22. end;end;end;end;
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-markdown/set/type/markdown.rb ~~

card/tmpsets/set/mod025-card-mod-prosemirror_editor/all/prosemirror_editor.rb

75.0% lines covered

8 relevant lines. 6 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (ProsemirrorEditor)
  4. #
  5. 1 module ProsemirrorEditor;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/all/prosemirror_editor.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def prosemirror_editor_input
  10. wrap_with :div, id: unique_id, class: "prosemirror-editor" do
  11. hidden_field :content, class: "d0-card-content", value: card.content
  12. end
  13. end
  14. end
  15. end;end;end;end;
  16. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/all/prosemirror_editor.rb ~~

card/tmpsets/set/mod025-card-mod-prosemirror_editor/self/script_prosemirror.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptProsemirror"
  4. #
  5. 1 module ScriptProsemirror;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/script_prosemirror.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptMods.add_item :script_prosemirror
  10. 1 Self::InputOptions.add_to_basket :options, "prosemirror editor"
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/script_prosemirror.rb ~~

card/tmpsets/set/mod025-card-mod-prosemirror_editor/self/script_prosemirror_config.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptProsemirrorConfig"
  4. #
  5. 1 module ScriptProsemirrorConfig;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/script_prosemirror_config.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptEditors.add_item :script_prosemirror_config
  10. 1 All::Head::HtmlFormat.add_to_basket :mod_js_config,
  11. [:prose_mirror, "setProseMirrorConfig"]
  12. end;end;end;end;
  13. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/script_prosemirror_config.rb ~~

card/tmpsets/set/mod025-card-mod-prosemirror_editor/self/style_prosemirror.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "StyleProsemirror"
  4. #
  5. 1 module StyleProsemirror;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/style_prosemirror.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::StyleMods.add_item :style_prosemirror
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-prosemirror_editor/set/self/style_prosemirror.rb ~~

card/tmpsets/set/mod026-card-mod-recaptcha/all/recaptcha.rb

56.0% lines covered

50 relevant lines. 28 lines covered and 22 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (Recaptcha)
  4. #
  5. 1 module Recaptcha;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/all/recaptcha.rb"; end
  8. RECAPTCHA_ERROR_CODES = { # LOCALIZE
  9. 1 "missing-input-secret" => "secret parameter is missing",
  10. "invalid-input-secret" => "secret parameter is invalid or malformed",
  11. "missing-input-response" => "response parameter is missing",
  12. "invalid-input-response" => "response parameter is invalid or malformed",
  13. "bad-request" => "request is invalid or malformed"
  14. }
  15. 1 def human?
  16. result = JSON.parse recaptcha_response
  17. return if recaptcha_success?(result)
  18. add_recaptcha_errors result["error-codes"]
  19. end
  20. 1 def recaptcha_on?
  21. 235 recaptcha_keys? &&
  22. Env[:controller] &&
  23. !Auth.signed_in? &&
  24. !Auth.always_ok? &&
  25. !Auth.needs_setup? &&
  26. Card::Rule.toggle(rule(:captcha))
  27. end
  28. 1 def add_recaptcha_errors error_codes
  29. if error_codes.present?
  30. error_codes.each do |code|
  31. errors.add :recaptcha, RECAPTCHA_ERROR_CODES.fetch(code, code)
  32. end
  33. else
  34. errors.add :recaptcha, "Looks like you are not a human" # LOCALIZE
  35. end
  36. end
  37. 1 def recaptcha_success? result
  38. result['success'] &&
  39. (result['score'].to_f >= Cardio.config.recaptcha_minimum_score) &&
  40. (result['action'].to_sym == action.to_sym)
  41. end
  42. 1 def recaptcha_response
  43. ::Recaptcha.get({ secret: Card.config.recaptcha_secret_key,
  44. response: Env.params[:recaptcha_token] }, {})
  45. end
  46. 1 def recaptcha_keys?
  47. 235 Card.config.recaptcha_site_key && Card.config.recaptcha_secret_key
  48. end
  49. 1 event :recaptcha, :validate, when: :validate_recaptcha? do
  50. handle_recaptcha_config_errors do
  51. Env[:recaptcha_used] = true
  52. human?
  53. end
  54. end
  55. 1 def handle_recaptcha_config_errors
  56. if Env.params[:recaptcha_token] == "grecaptcha-undefined"
  57. errors.add "recaptcha", "needs correct v3 configuration" # LOCALILZE
  58. elsif Env.params[:recaptcha_token] == "recaptcha-token-field-missing"
  59. raise Card::Error, "recaptcha token field missing" # LOCALILZE
  60. else
  61. yield
  62. end
  63. end
  64. 1 def validate_recaptcha?
  65. 219 !@supercard && !Env[:recaptcha_used] && recaptcha_on?
  66. end
  67. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  68. 1 def recaptcha_token action
  69. output [
  70. javascript_include_tag(recaptcha_script_url),
  71. hidden_field_tag("recaptcha_token", "",
  72. "data-site-key": Card.config.recaptcha_site_key,
  73. "data-action": action,
  74. class: "_recaptcha-token")
  75. ]
  76. end
  77. 1 def recaptcha_script_url
  78. "https://www.google.com/recaptcha/api.js?render=#{Card.config.recaptcha_site_key}"
  79. end
  80. 1 def hidden_form_tags action, opts
  81. 341 return super unless recaptcha?(opts)
  82. super + recaptcha_token(action)
  83. end
  84. 1 def card_form_html_opts action, opts={}
  85. 341 super
  86. 341 opts["data-recaptcha"] ||= "on" if recaptcha?(opts)
  87. 341 opts
  88. end
  89. 1 def recaptcha? opts
  90. 682 card.recaptcha_on? && opts[:recaptcha] != :off
  91. end
  92. end
  93. end;end;end;end;
  94. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/all/recaptcha.rb ~~

card/tmpsets/set/mod026-card-mod-recaptcha/self/admin_info.rb

56.25% lines covered

16 relevant lines. 9 lines covered and 7 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "AdminInfo"
  4. #
  5. 1 module AdminInfo;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/admin_info.rb"; end
  8. 1 add_to_basket :warnings, :recaptcha_config_issues
  9. 1 def recaptcha_config_issues?
  10. RecaptchaCard.using_defaults?
  11. end
  12. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  13. 1 def recaptcha_config_issues_message
  14. warning =
  15. if Card::Env.localhost?
  16. # %(Your captcha is currently working with temporary settings.
  17. # This is fine for a local installation, but you will need new
  18. # recaptcha keys if you want to make this site public.)
  19. I18n.t(:captcha_temp, scope: "mod.admin.set.self.admin_info",
  20. recaptcha_link: add_recaptcha_keys_link)
  21. else
  22. # %(You are configured to use [[*captcha]], but for that to work
  23. # you need new recaptcha keys.)
  24. I18n.t(:captcha_keys, scope: "mod.admin.set.self.admin_info",
  25. recaptcha_link: add_recaptcha_keys_link,
  26. captcha_link: link_to_card(:captcha))
  27. end
  28. <<-HTML
  29. <p>#{warning}</p>
  30. HTML
  31. end
  32. 1 def add_recaptcha_keys_link
  33. link_text = I18n.t :recaptcha_keys, scope: "mod.admin.set.self.admin_info"
  34. Card[:recaptcha_settings]&.format&.edit_link link_text: link_text
  35. end
  36. end
  37. end;end;end;end;
  38. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/admin_info.rb ~~

card/tmpsets/set/mod026-card-mod-recaptcha/self/recaptcha_proxy.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "RecaptchaProxy"
  4. #
  5. 1 module RecaptchaProxy;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_proxy.rb"; end
  8. 1 event :set_recaptcha_proxy, :finalize do
  9. Card.config.recaptcha_proxy = content
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_proxy.rb ~~

card/tmpsets/set/mod026-card-mod-recaptcha/self/recaptcha_secret_key.rb

66.67% lines covered

9 relevant lines. 6 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "RecaptchaSecretKey"
  4. #
  5. 1 module RecaptchaSecretKey;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_secret_key.rb"; end
  8. 1 event :validate_recaptcha_secret_key, :validate do
  9. return if content.match?(/^[a-zA-Z0-9\-_]*$/)
  10. errors.add :content, "invalid key" # LOCALIZE
  11. end
  12. 1 event :set_recaptcha_secret_key, :finalize do
  13. Card.config.recaptcha_secret_key = content
  14. end
  15. end;end;end;end;
  16. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_secret_key.rb ~~

card/tmpsets/set/mod026-card-mod-recaptcha/self/recaptcha_settings.rb

85.71% lines covered

7 relevant lines. 6 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "RecaptchaSettings"
  4. #
  5. 1 module RecaptchaSettings;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_settings.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def raw_help_text
  10. # LOCALIZE
  11. "Register your domain at Google's [[http://google.com/recaptcha|reCAPTCHA service]] "\
  12. "and enter your site key and secret key below.<br>"\
  13. "If you want to turn catchas off then change all [[*captcha|captcha rules]] to 'no'."
  14. end
  15. # def instructions title, steps
  16. # steps = list_tag steps, ordered: true
  17. # "#{title}#{steps}"
  18. # end
  19. #
  20. # <h5>#{instructions}</h5>
  21. # #{howto_add_new_recaptcha_keys}
  22. # #{howto_turn_captcha_off}
  23. #
  24. # def howto_add_new_recaptcha_keys
  25. # instructions(
  26. # I18n.t(:howto_add_keys, scope: "mod.admin.set.self.admin_info"),
  27. # [
  28. # I18n.t(:howto_register,
  29. # scope: "mod.admin.set.self.admin_info",
  30. # recaptcha_link: link_to_resource("http://google.com/recaptcha")),
  31. # I18n.t(:howto_add,
  32. # scope: "mod.admin.set.self.admin_info",
  33. # recaptcha_settings: link_to_card(:recaptcha_settings))
  34. # ]
  35. # )
  36. # end
  37. #
  38. # def howto_turn_captcha_off
  39. # instructions(
  40. # I18n.t(:howto_turn_off, scope: "mod.admin.set.self.admin_info"),
  41. # [
  42. # I18n.t(:howto_go,
  43. # scope: "mod.admin.set.self.admin_info",
  44. # captcha_card: link_to_card(:captcha)),
  45. # I18n.t(:howto_update,
  46. # scope: "mod.admin.set.self.admin_info")
  47. # ]
  48. # )
  49. # end
  50. end
  51. end;end;end;end;
  52. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_settings.rb ~~

card/tmpsets/set/mod026-card-mod-recaptcha/self/recaptcha_site_key.rb

66.67% lines covered

9 relevant lines. 6 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "RecaptchaSiteKey"
  4. #
  5. 1 module RecaptchaSiteKey;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_site_key.rb"; end
  8. 1 event :validate_recaptcha_site_key, :validate do
  9. return if content.match?(/^[a-zA-Z0-9\-_]*$/)
  10. errors.add :content, "invalid key" # LOCALIZE
  11. end
  12. 1 event :set_recaptcha_site_key, :finalize do
  13. Card.config.recaptcha_site_key = content
  14. end
  15. end;end;end;end;
  16. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-recaptcha/set/self/recaptcha_site_key.rb ~~

card/tmpsets/set/mod027-card-mod-rules/right/self.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Self" cards
  4. #
  5. 1 module Self;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/right/self.rb"; end
  8. 1 def prototype_default_card
  9. left
  10. end
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/right/self.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rstar/rule_user.rb

87.5% lines covered

8 relevant lines. 7 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Rstar
  3. # Set: All "+*" cards (RuleUser)
  4. #
  5. 1 module RuleUser;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rstar/rule_user.rb"; end
  8. 1 def rule_user_name
  9. is_preference? ? name.trunk_name.tag : nil
  10. end
  11. 1 def rule_user
  12. 25 is_preference? ? self[-2] : nil
  13. end
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rstar/rule_user.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/bar_view.rb

69.39% lines covered

49 relevant lines. 34 lines covered and 15 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Rule
  3. # Set: All rule cards (BarView)
  4. #
  5. 1 module BarView;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/bar_view.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 bar_cols 6, 6
  10. 1 info_bar_cols 5, 4, 3
  11. 1 def existing_rule_card
  12. 4 @existing_rule_card ||= find_existing_rule_card
  13. end
  14. 1 view :bar, unknown: true do
  15. 1 voo.hide :bar_nav unless existing_rule_card
  16. 1 super()
  17. end
  18. 1 view :expanded_bar, unknown: true do
  19. super()
  20. end
  21. 1 view :one_line_content,
  22. wrap: { div: { class: "text-muted one-line" } }, unknown: true do
  23. 1 return render_mini_unknown unless existing_rule_card
  24. 1 with_nest_mode :compact do
  25. 1 one_line_content
  26. end
  27. end
  28. 1 view :raw_one_line_content,
  29. wrap: { div: { class: "text-muted one-line" } }, unknown: true do
  30. return render_mini_unknown unless existing_rule_card
  31. raw_one_line_content
  32. end
  33. 1 view :bar_bottom, unknown: true do
  34. if nest_mode == :edit
  35. current_rule_form
  36. else
  37. nest existing_rule_card, view: :core
  38. end
  39. end
  40. 1 view :bar_middle, unknown: true do
  41. rule_info
  42. end
  43. 1 view :bar_left, unknown: true do
  44. 1 super()
  45. end
  46. 1 view :bar_right, unknown: true do
  47. 1 voo.show?(:bar_bottom) ? rule_info : rule_short_content
  48. end
  49. 1 def rule_short_content
  50. 1 return "" unless existing_rule_card
  51. 1 nest existing_rule_card, { view: :one_line_content },
  52. set_context: card.name.trunk_name
  53. end
  54. 1 def bar_title
  55. 1 return super() if voo.show? :full_name
  56. linking_to_existing_rule { card.rule_setting_title }
  57. end
  58. # LOCALIZE
  59. 1 def rule_info
  60. return wrap_with(:em, "no existing #{setting_link} rule") unless existing_rule_card
  61. wrap_with :span,
  62. "#{rule_setting_link} rule that applies to "\
  63. "#{rule_set_link existing_rule_card}"
  64. end
  65. 1 def rule_setting_link
  66. link_to_card card.rule_setting, card.rule_setting_name
  67. end
  68. 1 def rule_set_link existing_rule
  69. count = link_to_card [card.rule_set, :by_name], card.rule_set.count
  70. "#{link_to_card card.rule_set, existing_rule.trunk&.label&.downcase} (#{count})"
  71. end
  72. 1 private
  73. 1 def linking_to_existing_rule
  74. return yield unless existing_rule_card && voo.show?(:toggle)
  75. link_to_view bar_title_toggle_view, yield
  76. end
  77. end
  78. end;end;end;end;
  79. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/bar_view.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/bridge_rules_editor.rb

72.22% lines covered

18 relevant lines. 13 lines covered and 5 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Rule
  3. # Set: All rule cards (BridgeRulesEditor)
  4. #
  5. 1 module BridgeRulesEditor;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/bridge_rules_editor.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :overlay_rule, cache: :never, unknown: true do
  10. wrap_with_overlay slot: breadcrumb_data("Rule editing", "rules") do
  11. current_rule_form
  12. end
  13. end
  14. 1 view :modal_rule, cache: :never, unknown: true,
  15. wrap: { modal: { title: ->(format) { format.render_title } } } do
  16. current_rule_form
  17. end
  18. 1 view :overlay_title do
  19. edit_rule_title
  20. end
  21. 1 view :help_text, unknown: true, cache: :never do
  22. 19 wrap_help_text [rule_based_help, setting_link].join(" ")
  23. end
  24. 1 def setting_link
  25. 19 wrap_with :div, class: "ml-auto" do
  26. 19 link_to_card card.rule_setting_name,
  27. " (#{card.rule_setting.count} #{card.rule_setting_title} rules)",
  28. class: "text-muted"
  29. end
  30. end
  31. end
  32. end;end;end;end;
  33. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/bridge_rules_editor.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/editor.rb

48.15% lines covered

54 relevant lines. 26 lines covered and 28 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Rule
  3. # Set: All rule cards (Editor)
  4. #
  5. 1 module Editor;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/editor.rb"; end
  8. 1 def left_type_for_nest_editor_set_selection
  9. return super unless is_template?
  10. case Card.fetch_id rule_set_pattern_name
  11. when TypeID
  12. rule_set.anchor_name
  13. when SelfID
  14. rule_set.anchor.type_name
  15. else
  16. super
  17. end
  18. end
  19. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  20. 1 attr_accessor :rule_context
  21. 1 view :rule_edit, cache: :never, unknown: true,
  22. wrap: { modal: { size: :large,
  23. title: :edit_rule_title,
  24. footer: "" } } do
  25. current_rule_form form_type: :modal
  26. end
  27. 1 view :rule_help, unknown: true, perms: :none, cache: :never do
  28. wrap_with :div, class: "alert alert-info rule-instruction" do
  29. rule_based_help
  30. end
  31. end
  32. 1 view :rule_bridge_link, unknown: true do
  33. opts = bridge_link_opts(class: "edit-rule-link nav-link",
  34. "data-toggle": "pill",
  35. "data-cy": "#{setting_title.to_name.key}-pill")
  36. opts[:path].delete(:layout)
  37. link_to_view(:overlay_rule, (setting_title + short_help_text), opts)
  38. end
  39. 1 def edit_link_view
  40. 2 :rule_edit
  41. end
  42. 1 def edit_rule_title
  43. output [
  44. wrap_with(:h5, setting_title, class: "title font-weight-bold")
  45. # render_overlay_rule_help
  46. ]
  47. end
  48. 1 def current_rule
  49. if params[:assign]
  50. card
  51. elsif (existing = find_existing_rule_card)
  52. existing
  53. else
  54. card
  55. end
  56. end
  57. 1 def quick_editor
  58. rule_content_formgroup
  59. end
  60. 1 def setting_title
  61. card.name.tag.tr "*", ""
  62. end
  63. 1 def short_help_text
  64. "<div class=\"help-text\">#{card.short_help_text}</div>"
  65. end
  66. 1 def rule_set_description
  67. 2 card.rule_set.follow_label
  68. end
  69. 1 def rules_type_formgroup
  70. return unless card.right.rule_type_editable
  71. success = @edit_rule_success
  72. wrap_type_formgroup do
  73. type_field(
  74. href: path(mark: success[:id], view: :rule_form, assign: true),
  75. class: "type-field rule-type-field live-type-field",
  76. "data-remote" => true
  77. )
  78. end
  79. end
  80. 1 def rule_content_formgroup
  81. _render_content_formgroup hide: :conflict_tracker
  82. end
  83. 1 def current_set_key
  84. card.new_card? ? Card.quick_fetch(:all).name.key : card.rule_set_key
  85. end
  86. 1 private
  87. 1 def find_existing_rule_card
  88. 2 card.new_card? ? existing_rule_from_prototype : card
  89. end
  90. # self.card is a POTENTIAL rule; it quacks like a rule but may or may not exist.
  91. # This generates a prototypical member of the POTENTIAL rule's set
  92. # and returns that member's ACTUAL rule for the POTENTIAL rule's setting
  93. 1 def existing_rule_from_prototype
  94. return unless (setting = card.right)
  95. card.set_prototype.rule_card setting.codename, user: card.rule_user
  96. end
  97. end
  98. end;end;end;end;
  99. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/editor.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/html_views.rb

88.89% lines covered

9 relevant lines. 8 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Rule
  3. # Set: All rule cards (HtmlViews)
  4. #
  5. 1 module HtmlViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/html_views.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :core do
  10. # Rule cards that are searches are usual right structures and refer to the left
  11. # in the search query. In that case the search doesn't work
  12. # properly in the context of the rule card itself. Hence we show the query syntax
  13. # and not the search result.
  14. 69 if card.type_id == SearchTypeID
  15. render_raw
  16. else
  17. 69 super()
  18. end
  19. end
  20. end
  21. end;end;end;end;
  22. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/html_views.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/quick_editor.rb

57.14% lines covered

21 relevant lines. 12 lines covered and 9 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Rule
  3. # Set: All rule cards (QuickEditor)
  4. #
  5. 1 module QuickEditor;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/quick_editor.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :quick_edit, unknown: true, wrap: :slot do
  10. quick_edit
  11. end
  12. 1 view :quick_edit_success do
  13. set_info true
  14. end
  15. 1 def quick_edit
  16. haml :quick_edit
  17. end
  18. 1 def quick_form
  19. card_form :update, quick_form_opts do
  20. quick_editor
  21. end
  22. end
  23. 1 def quick_form_opts
  24. { "data-slot-selector": ".set-info.card-slot",
  25. success: { view: :quick_edit_success } }
  26. end
  27. 1 def set_info notify_change=nil
  28. wrap true, class: "set-info" do
  29. haml :set_info, notify_change: notify_change
  30. end
  31. end
  32. 1 def undo_button
  33. link_to "undo", method: :post, rel: "nofollow", remote: true,
  34. class: "btn btn-secondary ml-2 btn-sm btn-reduced-padding slotter",
  35. "data-slot-selector": ".card-slot.quick_edit-view",
  36. path: { action: :update,
  37. revert_actions: [card.last_action_id],
  38. revert_to: :previous }
  39. end
  40. end
  41. end;end;end;end;
  42. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/quick_editor.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/rule_form.rb

37.14% lines covered

35 relevant lines. 13 lines covered and 22 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Rule
  3. # Set: All rule cards (RuleForm)
  4. #
  5. 1 module RuleForm;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :rule_form, cache: :never, unknown: true do
  10. @success_view ||= :open
  11. @rule_context ||= card
  12. @form_type ||= :overlay
  13. wrap do
  14. edit_rule_form @success_view do
  15. [
  16. hidden_tags(success: @edit_rule_success),
  17. haml(:rule_form)
  18. ].join
  19. end
  20. end
  21. end
  22. 1 def form_type
  23. @form_type || :overlay
  24. end
  25. 1 def current_rule_form success_view: :overlay_rule, form_type: :overlay
  26. current_rule_format = subformat current_rule
  27. current_rule_format.rule_form success_view, card, form_type
  28. end
  29. 1 def rule_form success_view, rule_context, form_type=:overlay
  30. validate_form_type form_type
  31. @rule_context = rule_context
  32. @form_type = form_type
  33. @success_view = success_view
  34. render_rule_form
  35. end
  36. 1 def validate_form_type form_type
  37. return if form_type.in? %i[overlay modal]
  38. raise "invalid rule_form type: #{form_type}; has to be overlay or modal"
  39. end
  40. 1 def edit_rule_form success_view, &block
  41. @rule_context ||= card
  42. @edit_rule_success = edit_rule_success(success_view)
  43. action_args = { action: :update, no_mark: true }
  44. card_form action_args, rule_form_args, &block
  45. end
  46. 1 def rule_form_args
  47. { class: "card-rule-form", "data-slotter-mode": "update-origin" }
  48. end
  49. 1 def edit_rule_success view="overlay_rule"
  50. { id: @rule_context.name.url_key,
  51. view: view }
  52. end
  53. end
  54. end;end;end;end;
  55. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/buttons.rb

56.52% lines covered

23 relevant lines. 13 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Rule; module RuleForm;
  3. # Set: All rule cards (RuleForm, Buttons)
  4. #
  5. 1 module Buttons;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/buttons.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def edit_rule_buttons
  10. wrap_with(:div, class: "button-area") do
  11. [
  12. standard_save_button(class: "_rule-submit-button"),
  13. standard_save_and_close_button(class: "_rule-submit-button", close: form_type),
  14. edit_rule_cancel_button,
  15. edit_rule_delete_button
  16. ]
  17. end
  18. end
  19. 1 def edit_rule_cancel_button
  20. send "#{form_type}_close_button", "Cancel", situation: "secondary", class: "btn-sm"
  21. end
  22. 1 def edit_rule_delete_button args={}
  23. return if card.new_card?
  24. delete_opts = {
  25. confirm: delete_confirm(args[:fallback_set]),
  26. # success: @edit_rule_success,
  27. no_success: true,
  28. "data-slotter-mode": "silent-success",
  29. class: "_close-#{form_type}-on-success"
  30. }
  31. delete_opts["data-slot-selector"] = slot_selector if args[:slot_selector]
  32. wrap_with :span, class: "rule-delete-section" do
  33. delete_button delete_opts
  34. end
  35. end
  36. 1 def delete_confirm fallback_set
  37. 2 setting = card.rule_setting_name
  38. 2 if fallback_set && (fallback_set_card = Card.fetch(fallback_set))
  39. "Deleting will revert to #{setting} rule for #{fallback_set_card.label}"
  40. else
  41. 2 "Are you sure you want to delete the #{setting} rule for #{rule_set_description}?"
  42. end
  43. end
  44. 1 def edit_rule_submit_button
  45. submit_button class: "_rule-submit-button"
  46. end
  47. end
  48. end;end;end;end;end;
  49. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/buttons.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/form_elements.rb

33.33% lines covered

33 relevant lines. 11 lines covered and 22 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Rule; module RuleForm;
  3. # Set: All rule cards (RuleForm, FormElements)
  4. #
  5. 1 module FormElements;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/form_elements.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. #### DEPRECATED
  10. 1 def rule_set_selection
  11. wrap_with :div, class: "set-list" do
  12. [rule_set_formgroup, related_set_formgroup]
  13. end
  14. end
  15. 1 def rule_set_formgroup
  16. tag = @rule_context.rule_user_setting_name
  17. narrower = []
  18. option_list "Set" do
  19. rule_set_options.map do |set_name, state|
  20. rule_set_radio_button set_name, tag, state, narrower
  21. end
  22. end
  23. end
  24. 1 def related_set_formgroup
  25. related_sets = related_sets_in_context
  26. return "" unless related_sets&.present?
  27. tag = @rule_context.rule_user_setting_name
  28. option_list "related set" do
  29. related_rule_radios related_sets, tag
  30. end
  31. end
  32. 1 def related_sets_in_context
  33. set_context = @rule_context.rule_set_name
  34. set_context && Card.fetch(set_context).prototype.related_sets
  35. end
  36. 1 def option_list title
  37. formgroup title, input: "set", class: "col-xs-6", help: false do
  38. wrap_with :ul do
  39. wrap_each_with(:li, class: "radio") { yield }
  40. end
  41. end
  42. end
  43. 1 def related_rule_radios related_sets, tag
  44. related_sets.map do |set_name, _label|
  45. rule_name = "#{set_name}+#{tag}"
  46. state = Card.exists?(rule_name) ? :exists : nil
  47. rule_radio set_name, state do
  48. radio_button :name, rule_name
  49. end
  50. end
  51. end
  52. end
  53. end;end;end;end;end;
  54. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/form_elements.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/rule_set_radio.rb

36.17% lines covered

47 relevant lines. 17 lines covered and 30 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Rule
  3. # Set: All rule cards (RuleForm)
  4. #
  5. #! no set module
  6. 1 module RuleForm;
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/rule_set_radio.rb"; end
  8. 1 class RuleSetRadio
  9. 1 attr_reader :format
  10. 1 delegate :link_to_card, :radio_button, :wrap_with, :icon_tag,
  11. to: :format
  12. # @param state [:current, :overwritten]
  13. 1 def initialize format, set_name, tag, state
  14. @format = format
  15. @card = format.card
  16. @set_name = set_name
  17. @tag = tag
  18. @state = state
  19. end
  20. 1 def html narrower
  21. @narrower_rules = narrower
  22. rule_radio do
  23. radio_text = "#{@set_name}+#{@tag}"
  24. radio_button :name, radio_text, checked: false, warning: warning
  25. end
  26. end
  27. 1 private
  28. 1 def current?
  29. @state == :current
  30. end
  31. 1 def overwritten?
  32. @state == :overwritten
  33. end
  34. 1 def rule_radio
  35. label_classes = ["set-label", ("current-set-label" if current?)]
  36. icon = icon_tag "open_in_new", "link-muted"
  37. wrap_with :label, class: label_classes.compact.join(" ") do
  38. [yield, label, link_to_card(@set_name, icon, target: "decko_set")]
  39. end
  40. end
  41. 1 def label
  42. label = Card.fetch(@set_name).label
  43. label += " <em>#{extra_info}</em>".html_safe if extra_info
  44. label
  45. end
  46. 1 def extra_info
  47. case @state
  48. when :current
  49. "(current)"
  50. when :overwritten, :exists
  51. link_to_card "#{@set_name}+#{@card.rule_user_setting_name}", "(#{@state})",
  52. target: "_blank"
  53. end
  54. end
  55. 1 def warning
  56. if @set_name == "*all"
  57. "This rule will affect all cards! Are you sure?"
  58. else
  59. narrower_warning
  60. end
  61. end
  62. # warn user if rule change won't have a effect on the current card
  63. # because there is a narrower rule
  64. 1 def narrower_warning
  65. return unless @state.in? %i[current overwritten]
  66. @narrower_rules << Card.fetch(@set_name).uncapitalized_label
  67. return unless @state == :overwritten
  68. narrower_warning_message
  69. end
  70. 1 def narrower_warning_message
  71. plural = @narrower_rules.size > 1 ? "s" : ""
  72. "This rule will not have any effect on this card unless you delete " \
  73. "the narrower rule#{plural} for #{@narrower_rules.to_sentence}."
  74. end
  75. end
  76. end;end;end;end;
  77. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/rule_set_radio.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/rule_form/set_selection.rb

39.13% lines covered

23 relevant lines. 9 lines covered and 14 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Rule; module RuleForm;
  3. # Set: All rule cards (RuleForm, SetSelection)
  4. #
  5. 1 module SetSelection;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/set_selection.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def bridge_rule_set_selection
  10. wrap_with :div, class: "set-list" do
  11. bridge_rule_set_formgroup
  12. end
  13. end
  14. 1 def bridge_rule_set_formgroup
  15. tag = @rule_context.rule_user_setting_name
  16. narrower = []
  17. bridge_option_list "Set" do
  18. rule_set_options.map do |set_name, state|
  19. RuleSetRadio.new(self, set_name, tag, state).html narrower
  20. end
  21. end
  22. end
  23. 1 def bridge_option_list title
  24. index = -1
  25. formgroup title, input: "set", class: "col-xs-6", help: false do
  26. yield.inject("") do |res, radio|
  27. index += 1
  28. # TODO
  29. if false # index.in? [2,3]
  30. wrap_with(:li, radio, class: "radio") + res
  31. else
  32. wrap_with :ul do
  33. wrap_with(:li, (radio + res), class: "radio")
  34. end
  35. end
  36. end
  37. end
  38. end
  39. 1 def rule_set_options
  40. @rule_set_options ||= @rule_context.set_options
  41. end
  42. end
  43. end;end;end;end;end;
  44. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rule_form/set_selection.rb ~~

card/tmpsets/set/mod027-card-mod-rules/rule/rules.rb

52.54% lines covered

59 relevant lines. 31 lines covered and 28 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Rule
  3. # Set: All rule cards (Rules)
  4. #
  5. 1 module Rules;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rules.rb"; end
  8. 1 event :save_recently_edited_settings, :integrate, on: :save, changed: %i[type content] do
  9. 37 if (recent = Card[:recent_settings])
  10. 37 recent.insert_item 0, name.right
  11. 37 attach_subcard recent
  12. end
  13. end
  14. 1 def rule_set_key
  15. rule_set_name.key
  16. end
  17. 1 def rule_set_name
  18. if is_preference?
  19. name.trunk_name.trunk_name
  20. else
  21. name.trunk_name
  22. end
  23. end
  24. 1 def rule_set_pattern_name
  25. rule_set_name.tag_name
  26. end
  27. 1 def rule_set
  28. 2 if is_preference?
  29. self[0..-3]
  30. else
  31. 2 trunk
  32. end
  33. end
  34. 1 def rule_setting
  35. 19 right
  36. end
  37. 1 def rule_setting_name
  38. 40 name.tag
  39. end
  40. 1 def short_help_text
  41. Card[rule_setting_name].short_help_text
  42. end
  43. 1 def rule_setting_title
  44. 19 rule_setting_name.tr "*", ""
  45. end
  46. 1 def rule_user_setting_name
  47. if is_preference?
  48. "#{rule_user_name}+#{rule_setting_name}"
  49. else
  50. rule_setting_name
  51. end
  52. end
  53. # ~~~~~~~~~~ determine the set options to which a user can apply the rule.
  54. 1 def set_options
  55. @set_options ||= [].tap do |set_options|
  56. set_option_candidates.each do |set_name|
  57. set_options << [set_name, state_of_set(set_name)]
  58. end
  59. end
  60. end
  61. # the narrowest rule should be the one attached to the set being viewed.
  62. # So, eg, if you're looking at the '*all plus' set, you shouldn't
  63. # have the option to create rules based on arbitrary narrower sets, though
  64. # narrower sets will always apply to whatever prototype we create
  65. 1 def first_set_option_index candidates
  66. new_card? ? 0 : candidates.index { |c| c.to_name.key == rule_set_key }
  67. end
  68. 1 def set_prototype
  69. if is_preference?
  70. self[0..-3].prototype
  71. else
  72. trunk.prototype
  73. end
  74. end
  75. 1 private
  76. 1 def set_option_candidates
  77. candidates = set_prototype.set_names
  78. first = first_set_option_index candidates
  79. candidates[first..-1]
  80. end
  81. 1 def state_of_set set_name
  82. @sets_with_existing_rules ||= 0
  83. if rule_for_set? set_name
  84. @sets_with_existing_rules += 1
  85. state_of_existing_set
  86. else
  87. state_of_nonexisting_set
  88. end
  89. end
  90. 1 def state_of_existing_set
  91. @sets_with_existing_rules == 1 ? :current : :overwritten
  92. end
  93. 1 def state_of_nonexisting_set
  94. @sets_with_existing_rules == 1 ? :current : :overwritten
  95. end
  96. 1 def rule_for_set? set_name
  97. Card.exists?("#{set_name}+#{rule_user_setting_name}")
  98. end
  99. end;end;end;end;
  100. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/rule/rules.rb ~~

card/tmpsets/set/mod027-card-mod-rules/self/script_rules.rb

100.0% lines covered

6 relevant lines. 6 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptRules"
  4. #
  5. 1 module ScriptRules;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/self/script_rules.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptMods.add_item :script_rules
  10. end;end;end;end;
  11. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/self/script_rules.rb ~~

card/tmpsets/set/mod027-card-mod-rules/type/set.rb

56.45% lines covered

62 relevant lines. 35 lines covered and 27 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Type
  3. # Set: All "Set" cards
  4. #
  5. 1 module Set;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set.rb"; end
  8. 1 include_set Type::SearchType
  9. 1 def anchor_name
  10. 8 name.left_name
  11. end
  12. 1 def anchor
  13. Card[anchor_name]
  14. end
  15. 1 def pattern_name
  16. name.tag_name
  17. end
  18. 1 def pattern
  19. 44 tag
  20. end
  21. 1 def inheritable?
  22. junction_only? || (anchor_name&.junction? && self_set?)
  23. end
  24. 1 def self_set?
  25. pattern_name == Card::Set::Self.pattern.key
  26. end
  27. 1 def subclass_for_set
  28. 44 current_set_pattern_code = pattern.codename
  29. 196 Card.set_patterns.find { |set| set.pattern_code == current_set_pattern_code }
  30. end
  31. 1 def junction_only?
  32. @junction_only.nil? ? (@junction_only = subclass_for_set.junction_only) : @junction_only
  33. end
  34. 1 def label
  35. klass = subclass_for_set
  36. klass ? klass.label(anchor_name) : ""
  37. end
  38. 1 def uncapitalized_label
  39. label = label.to_s
  40. return label unless label[0]
  41. label[0] = label[0].downcase
  42. label
  43. end
  44. 1 def rule_cache_key_base
  45. 954 if (l = left) && (r = right)
  46. 472 "#{l.id}+#{Codename[r.id]}"
  47. else
  48. 482 Codename[id].to_s
  49. end
  50. end
  51. 1 def all_user_ids_with_rule_for setting_code
  52. 954 Card::Rule.all_user_ids_with_rule_for self, setting_code
  53. end
  54. 1 def setting_codenames_by_group
  55. result = {}
  56. Card::Setting.groups.each do |group, settings|
  57. visible_settings =
  58. settings.reject { |s| !s || !s.applies_to_cardtype(prototype.type_id) }
  59. result[group] = visible_settings.map(&:codename) unless visible_settings.empty?
  60. end
  61. result
  62. end
  63. 1 def visible_setting_codenames
  64. @visible_setting_codenames ||= visible_settings.map(&:codename)
  65. end
  66. 1 def visible_settings group=nil, cardtype_id=nil
  67. cardtype_id ||= prototype.type_id
  68. settings =
  69. (group && Card::Setting.groups[group]) || Card::Setting.groups.values.flatten.compact
  70. settings.reject do |setting|
  71. !setting || !setting.applies_to_cardtype(cardtype_id)
  72. end
  73. end
  74. 1 def broader_sets
  75. prototype.set_names[1..-1]
  76. end
  77. 1 def prototype
  78. 8 opts = subclass_for_set.prototype_args anchor_name
  79. 8 Card.fetch opts[:name], new: opts
  80. end
  81. 1 def prototype_default_type_id
  82. prototype_default_card.type_id
  83. end
  84. 1 def prototype_default_card
  85. prototype.rule_card(:default)
  86. end
  87. 1 def related_sets with_self=false
  88. if subclass_for_set.anchorless?
  89. prototype.related_sets with_self
  90. else
  91. left(new: {}).related_sets with_self
  92. end
  93. end
  94. end;end;end;end;
  95. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set.rb ~~

card/tmpsets/set/mod027-card-mod-rules/type/set/html_views.rb

58.62% lines covered

29 relevant lines. 17 lines covered and 12 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Type; module Set;
  3. # Set: All "Set+HtmlViews" cards (HtmlViews)
  4. #
  5. 1 module HtmlViews;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 before :open do
  10. voo.hide :template_closer
  11. end
  12. 1 view :core, cache: :never do
  13. filtered_rule_list :bar_rule_list
  14. end
  15. 1 view :nest_rules, cache: :never, unknown: true, wrap: :slot do
  16. filtered_rule_list :quick_edit_rule_list, :field_related_rules, :related, mark: ""
  17. end
  18. 1 view :modal_nest_rules, cache: :never, unknown: true,
  19. wrap: { modal: { title: "Rules for nest" } } do
  20. filtered_rule_list :quick_edit_rule_list, :field_related_rules, :self
  21. end
  22. 1 view :bridge_rules_tab, cache: :never do
  23. filtered_rule_list :pill_rule_list, :common, :related, mark: ""
  24. end
  25. 1 def filtered_rule_list view, *filter_args
  26. [rules_filter(view, *filter_args),
  27. render(view)]
  28. end
  29. 1 view :set_label do
  30. wrap_with :strong, card.label, class: "set-label"
  31. end
  32. 1 Card::Setting.groups.each_key do |group_key|
  33. 7 view group_key.to_sym do
  34. next unless card.visible_settings(group_key).present?
  35. haml :group_panel, group_key: group_key
  36. end
  37. end
  38. 1 def setting_group default=:common
  39. voo&.filter&.to_sym || params[:group]&.to_sym || default
  40. end
  41. 1 view :input do
  42. "Cannot currently edit Sets" # LOCALIZE
  43. end
  44. 1 view :one_line_content, wrap: {} do
  45. ""
  46. end
  47. end
  48. end;end;end;end;end;
  49. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views.rb ~~

card/tmpsets/set/mod027-card-mod-rules/type/set/html_views/rule_lists.rb

50.0% lines covered

26 relevant lines. 13 lines covered and 13 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 5 class Card; module Set; class Type; module Set;; module HtmlViews;
  3. # Set: All "Set+HtmlViews" cards (HtmlViews, RuleLists)
  4. #
  5. 1 module RuleLists;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views/rule_lists.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :quick_edit_rule_list, cache: :never, wrap: { slot: { class: "rule-list" } } do
  10. quick_edit_rule_list setting_list_from_params(:field_related)
  11. end
  12. 1 view :bar_rule_list, cache: :never, wrap: { slot: { class: "rule-list" } } do
  13. bar_rule_list setting_list_from_params
  14. end
  15. 1 view :pill_rule_list, cache: :never, wrap: { slot: { class: "rule-list" } } do
  16. pill_rule_list setting_list_from_params
  17. end
  18. 1 def quick_edit_rule_list settings
  19. list_tag class: "nav nav-pills flex-column bridge-pills" do
  20. settings.map { |setting| rule_list_item setting, :quick_edit }
  21. end
  22. end
  23. 1 def pill_rule_list settings
  24. list_items =
  25. settings.map { |setting| rule_list_item setting, :rule_bridge_link }
  26. bridge_pills list_items
  27. end
  28. 1 def bar_rule_list settings
  29. list_items =
  30. settings.map { |setting| rule_list_item setting, :bar, hide: :full_name }
  31. list_items.join("\n").html_safe
  32. end
  33. 1 def rule_list_item setting, view, opts={}
  34. return "" unless show_view? setting
  35. rule_card = card.fetch setting, new: {}
  36. nest(rule_card, opts.merge(view: view)).html_safe
  37. end
  38. 1 def setting_list_from_params default=:common
  39. setting_list setting_group(default)
  40. end
  41. end
  42. end;end;end;end;end;end;
  43. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views/rule_lists.rb ~~

card/tmpsets/set/mod027-card-mod-rules/type/set/html_views/template.rb

100.0% lines covered

11 relevant lines. 11 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 5 class Card; module Set; class Type; module Set;; module HtmlViews;
  3. # Set: All "Set+HtmlViews" cards (HtmlViews, Template)
  4. #
  5. 1 module Template;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views/template.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :template_link, cache: :never do
  10. 21 wrap do
  11. 21 voo.title = parent.voo.nest_syntax if parent
  12. 21 "{{#{link_to_template_editor}}}"
  13. end
  14. end
  15. 1 def link_to_template_editor
  16. 21 link_to_view :modal_nest_rules, voo.title
  17. end
  18. end
  19. end;end;end;end;end;end;
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/html_views/template.rb ~~

card/tmpsets/set/mod027-card-mod-rules/type/set/rules_filter.rb

46.43% lines covered

28 relevant lines. 13 lines covered and 15 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Type; module Set;
  3. # Set: All "Set+RulesFilter" cards (RulesFilter)
  4. #
  5. 1 module RulesFilter;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/rules_filter.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def rules_filter view, selected_setting=nil, set_options=nil, path_opts={}
  10. form_tag path(path_opts.merge(view: view)),
  11. remote: true, method: "get", role: "filter",
  12. "data-slot-selector": ".card-slot.rule-list",
  13. class: classy("nodblclick slotter form-inline slim-select2 m-2") do
  14. output [
  15. label_tag(:view, icon_tag("filter_list"), class: "mr-2"),
  16. setting_select(selected_setting),
  17. set_select(set_options)
  18. ].flatten
  19. end
  20. end
  21. 1 def set_select set_options
  22. return filter_text.html_safe unless set_options
  23. [content_tag(:span, "rules that apply to set ...", class: "mx-2 small"),
  24. set_select_tag(set_options)]
  25. end
  26. 1 def setting_select selected=nil
  27. select_tag(:group, grouped_options_for_select(setting_options, selected),
  28. class: "_submit-on-select form-control",
  29. "data-select2-id": "#{unique_id}-#{Time.now.to_i}")
  30. end
  31. 1 def filter_text
  32. wrap_with :span, class: "mx-2 small" do
  33. "rules that apply to #{_render_set_label.downcase}" # LOCALIZE
  34. end
  35. end
  36. 1 def set_select_tag set_options=:related
  37. select_tag(:mark, set_select_options(set_options),
  38. class: "_submit-on-select form-control _close-rule-overlay-on-select",
  39. "data-minimum-results-for-search": "Infinity",
  40. "data-select2-id": "#{unique_id}-#{Time.now.to_i}")
  41. end
  42. 1 def selected_set
  43. params[:set]
  44. end
  45. 1 def set_select_options set_options
  46. options =
  47. if set_options == :related
  48. related_set_options
  49. else
  50. [[card.label, card.name.url_key]]
  51. end
  52. options_for_select(options, selected_set)
  53. end
  54. 1 def related_set_options
  55. card.related_sets(true).map do |name, label|
  56. [label, name.to_name.url_key]
  57. end
  58. end
  59. end
  60. end;end;end;end;end;
  61. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/rules_filter.rb ~~

card/tmpsets/set/mod027-card-mod-rules/type/set/setting_lists.rb

48.78% lines covered

41 relevant lines. 20 lines covered and 21 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class Type; module Set;
  3. # Set: All "Set+SettingLists" cards (SettingLists)
  4. #
  5. 1 module SettingLists;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/setting_lists.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 SETTING_OPTIONS = [["Common", :common_rules],
  10. ["All", :all_rules],
  11. ["Field", :field_related_rules],
  12. ["Recent", :recent_rules]].freeze
  13. 1 COMMON_SETTINGS = %i[create read update delete structure default guide].freeze
  14. 1 FIELD_SETTINGS = %i[default help].freeze
  15. 1 def setting_options
  16. [["Categories", SETTING_OPTIONS],
  17. ["Groups", Card::Setting.group_names.keys],
  18. ["Single rules", card.visible_setting_codenames]]
  19. end
  20. 1 def field_settings
  21. %i[default help input_type content_options content_option_view]
  22. end
  23. # @param val setting category, setting group or single setting
  24. 1 def setting_list val
  25. category_setting_list(val) || group_setting_list(val) || [val]
  26. end
  27. 1 def group_setting_list group
  28. card.visible_settings(group).map(&:codename) if Card::Setting.groups[group]
  29. end
  30. 1 def category_setting_list cat
  31. case cat
  32. when :all, :all_rules
  33. card.visible_setting_codenames.sort
  34. when :recent, :recent_rules
  35. recent_settings
  36. when :common, :common_rules
  37. card.visible_setting_codenames & COMMON_SETTINGS
  38. when :field_related, :field_related_rules
  39. field_related_settings
  40. when :nest_editor_field_related
  41. nest_editor_field_related_settings
  42. end
  43. end
  44. 1 def nest_editor_field_related_settings
  45. field_settings
  46. # & card.visible_settings(nil, card.prototype_default_type_id).map(&:codename)
  47. end
  48. 1 def field_related_settings
  49. field_settings # card.visible_setting_codenames &
  50. end
  51. 1 def recent_settings
  52. recent_settings = Card[:recent_settings].item_cards.map(&:codename)
  53. recent_settings.map(&:to_sym) & card.visible_setting_codenames
  54. end
  55. 1 view :all_rules_list do
  56. pill_rule_list card.visible_setting_codenames.sort
  57. end
  58. 1 view :recent_rules_list do
  59. recent_settings = Card[:recent_settings].item_cards.map(&:codename)
  60. settings = recent_settings.map(&:to_sym) & card.visible_setting_codenames
  61. pill_rule_list settings
  62. end
  63. 1 view :common_rules_list do
  64. settings = card.visible_setting_codenames & COMMON_SETTINGS # "&" = set intersection
  65. pill_rule_list settings
  66. end
  67. 1 view :field_related_rules_list do
  68. pill_rule_list field_related_settings
  69. end
  70. end
  71. end;end;end;end;end;
  72. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-rules/set/type/set/setting_lists.rb ~~

card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor.rb

52.38% lines covered

21 relevant lines. 11 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (ReferenceEditor)
  4. #
  5. # shared helper methods for link editor and nest editor
  6. 1 module ReferenceEditor;
  7. 1 extend Card::Set
  8. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor.rb"; end
  9. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  10. 1 def tinymce_id
  11. params[:tinymce_id]
  12. end
  13. 1 def tm_param key
  14. params[:"tm_snippet_#{key}"]
  15. end
  16. 1 def modal_tm_snippet_editor?
  17. @tm_snippet_editor_mode != :overlay
  18. end
  19. 1 private
  20. 1 def apply_tm_snippet_data snippet
  21. { "data-tinymce-id": tinymce_id }.tap do |data|
  22. apply_tm_snippet_var(data, :start) { tm_param :start }
  23. apply_tm_snippet_var(data, :size, :raw) { snippet.raw.size }
  24. data["data-dismiss"] = "modal" if modal_tm_snippet_editor?
  25. data["data-index"] = params["index"] if params["index"].present?
  26. end
  27. end
  28. 1 def apply_tm_snippet_var data, varname, paramname=nil
  29. return unless tm_param(paramname || varname).present?
  30. data[:"data-tm-snippet-#{varname}"] = yield
  31. end
  32. end
  33. end;end;end;end;
  34. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor.rb ~~

card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor/link_editor.rb

57.89% lines covered

19 relevant lines. 11 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module ReferenceEditor;
  3. # Set: All cards (ReferenceEditor, LinkEditor)
  4. #
  5. 1 module LinkEditor;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/link_editor.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :link_editor, cache: :never, unknown: true,
  10. wrap: {
  11. slot: { class: "_overlay d0-card-overlay card nodblclick" }
  12. } do
  13. link_editor :overlay
  14. end
  15. 1 view :modal_link_editor, cache: :never, unknown: true,
  16. wrap: { slot: { class: "nodblclick" } } do
  17. modal_link_editor
  18. end
  19. 1 def link_editor editor_mode
  20. @tm_snippet_editor_mode = editor_mode
  21. haml :reference_editor, ref_type: :link, editor_mode: @tm_snippet_editor_mode,
  22. apply_opts: link_apply_opts, snippet: link_snippet
  23. end
  24. 1 def modal_link_editor
  25. wrap_with :modal do
  26. link_editor :modal
  27. end
  28. end
  29. 1 def link_snippet
  30. @link_snippet ||= LinkParser.new params[:tm_snippet_raw]
  31. end
  32. 1 def link_apply_opts
  33. apply_tm_snippet_data link_snippet
  34. end
  35. end
  36. end;end;end;end;end;
  37. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/link_editor.rb ~~

card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor/link_editor/link_parser.rb

52.38% lines covered

21 relevant lines. 11 lines covered and 10 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module ReferenceEditor;
  3. # Set: All cards (ReferenceEditor, LinkEditor)
  4. #
  5. #! no set module
  6. 1 module LinkEditor;
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/link_editor/link_parser.rb"; end
  8. # Extracts all information needed to generate the link editor form
  9. # from a link syntax string
  10. 1 class LinkParser
  11. 1 attr_reader :name, :options, :field, :raw
  12. 1 def self.new link_string
  13. return super if link_string.is_a? String
  14. OpenStruct.new(name: "", options: {}, raw: "[[ ]]")
  15. end
  16. 1 def initialize link_string
  17. @raw = link_string
  18. link = Card::Content::Chunk::Link.new link_string, nil
  19. init_name link.name
  20. @options = link.options
  21. end
  22. 1 def title
  23. @options && @options[:title]
  24. end
  25. 1 def field?
  26. @field
  27. end
  28. 1 private
  29. 1 def init_name name
  30. @field = name.to_name.simple_relative?
  31. @name = @field ? name.to_s[1..-1] : name
  32. end
  33. end
  34. end;end;end;end;end;
  35. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/link_editor/link_parser.rb ~~

card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor/nest_editor.rb

33.33% lines covered

87 relevant lines. 29 lines covered and 58 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module ReferenceEditor;
  3. # Set: All cards (ReferenceEditor, NestEditor)
  4. #
  5. 1 module NestEditor;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/nest_editor.rb"; end
  8. 1 def left_type_for_nest_editor_set_selection
  9. type_name
  10. end
  11. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  12. # Card::View::Options.shark_keys - %i[nest_syntax nest_name items cache]
  13. # TODO: connect to Card::View::Options
  14. # (that way a mod can add an option that becomes available to nests)
  15. 1 view :nest_editor, cache: :never, unknown: true,
  16. wrap: {
  17. slot: { class: "_overlay d0-card-overlay card nodblclick" }
  18. } do
  19. nest_editor :overlay
  20. end
  21. 1 view :modal_nest_editor, cache: :never, unknown: true,
  22. wrap: { slot: { class: "nodblclick" } } do
  23. modal_nest_editor
  24. end
  25. 1 view :nest_content, perms: :create, cache: :never, unknown: true, wrap: :slot do
  26. if card.known?
  27. known_nest_content
  28. else
  29. unknown_nest_content
  30. end
  31. end
  32. 1 def nest_editor editor_mode
  33. @tm_snippet_editor_mode = editor_mode
  34. voo.hide :content_tab unless show_content_tab?
  35. haml :reference_editor, ref_type: :nest, editor_mode: @tm_snippet_editor_mode,
  36. apply_opts: nest_apply_opts,
  37. snippet: nest_snippet
  38. end
  39. 1 def nest_editor_tabs
  40. tab_hash = {}
  41. tab_hash[:content] = nest_content_tab if voo.show? :content_tab
  42. tab_hash.merge! options: haml(:_options, snippet: nest_snippet),
  43. rules: nest_rules_tab,
  44. help: haml(:_help)
  45. tabs tab_hash, default_active_tab
  46. end
  47. 1 def show_content_tab?
  48. !card.is_structure?
  49. end
  50. 1 def default_active_tab
  51. voo.show?(:content_tab) ? :content : :options
  52. end
  53. 1 def nest_content_tab
  54. name_dependent_slot do
  55. @nest_content_tab || nest(card.name.field(nest_snippet.name),
  56. view: :nest_content, hide: :guide)
  57. end
  58. end
  59. 1 def nest_rules_tab
  60. name_dependent_slot do
  61. nest(set_name_for_nest_rules, view: :nest_rules)
  62. end
  63. end
  64. 1 def name_dependent_slot
  65. result = [empty_nest_name_alert(nest_snippet.name.blank?)]
  66. result <<
  67. if nest_snippet.name.blank?
  68. content_tag :div, "", class: "card-slot" # placeholder
  69. else
  70. yield
  71. end
  72. result
  73. end
  74. 1 def empty_nest_name_alert show
  75. alert :warning, false, false,
  76. class: "mb-0 _empty-nest-name-alert #{'d-none' unless show}" do
  77. "nest name required" # LOCALIZE
  78. end
  79. end
  80. 1 def modal_nest_editor
  81. wrap_with :modal do
  82. nest_editor :modal
  83. end
  84. end
  85. 1 def nest_snippet
  86. @nest_snippet ||=
  87. Card::Reference::NestParser.new params[:tm_snippet_raw],
  88. default_nest_view, default_item_view
  89. end
  90. 1 def set_name_for_nest_rules
  91. nest_name = nest_snippet.name
  92. if (type_name = card.left_type_for_nest_editor_set_selection)
  93. [type_name, nest_name, :type_plus_right]
  94. else
  95. [nest_name, :right]
  96. end
  97. end
  98. 1 def default_nest_editor_item_options
  99. [[:view, default_item_view]]
  100. end
  101. 1 def nest_option_name_select selected=nil, level=0
  102. classes = "form-control form-control-sm _nest-option-name"
  103. classes += " _new-row" unless selected
  104. select_tag "nest_option_name_#{unique_id}",
  105. nest_option_name_select_options(selected, level),
  106. class: classes, id: nil
  107. # id: nil ensures that select2 generates its own unique identifier
  108. # that ensures that we can clone this tag without breaking select2
  109. end
  110. 1 def nest_option_name_select_options selected, level
  111. options = selected ? [] : ["--"]
  112. options += Card::Reference::NestParser::NEST_OPTIONS
  113. options_for_select(
  114. options, disabled: nest_option_name_disabled(selected, level),
  115. selected: selected
  116. )
  117. end
  118. 1 def nest_option_name_disabled selected, level
  119. disabled = nest_option_name_disabled_options level
  120. disabled = disabled&.map(&:first)
  121. disabled&.delete selected if selected
  122. disabled
  123. end
  124. 1 def nest_option_name_disabled_options level
  125. if level == 0
  126. nest_snippet.options
  127. else
  128. nest_snippet.item_options[level - 1] || default_nest_editor_item_options
  129. end
  130. end
  131. 1 def nest_apply_opts
  132. apply_tm_snippet_data nest_snippet
  133. end
  134. 1 def nest_option_value_select value=nil
  135. # select_tag "nest_option_value_#{unique_id}"
  136. text_field_tag "value", value,
  137. class: "_nest-option-value form-control form-control-sm",
  138. disabled: !value,
  139. id: nil
  140. end
  141. 1 def known_nest_content
  142. voo.hide! :cancel_button
  143. add_name_context
  144. with_nest_mode :edit do
  145. frame do
  146. [
  147. render_edit_inline
  148. ]
  149. end
  150. end
  151. end
  152. 1 def unknown_nest_content
  153. voo.hide! :guide
  154. voo.show! :new_type_formgroup
  155. new_view_frame_and_form buttons: new_image_buttons,
  156. success: { tinymce_id: Env.params[:tinymce_id] }
  157. end
  158. end
  159. end;end;end;end;end;
  160. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/nest_editor.rb ~~

card/tmpsets/set/mod028-card-mod-tinymce_editor/all/reference_editor/nest_image.rb

41.67% lines covered

36 relevant lines. 15 lines covered and 21 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 4 class Card; module Set; class All; module ReferenceEditor;
  3. # Set: All cards (ReferenceEditor, NestImage)
  4. #
  5. 1 module NestImage;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/nest_image.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :nest_image, unknown: true, cache: :never,
  10. wrap: {
  11. slot: { class: "_overlay d0-card-overlay card nodblclick" }
  12. } do
  13. nest_image_editor :overlay
  14. end
  15. 1 view :modal_nest_image, unknown: true, cache: :never,
  16. wrap: { slot: { class: "nodblclick" } } do
  17. nest_image_editor :modal
  18. end
  19. 1 view :new_image, perms: :create, unknown: true, cache: :never do
  20. new_view_frame_and_form new_image_form_opts
  21. end
  22. 1 def nest_image_editor editor_mode
  23. adapt_reference_editor_for_images
  24. nest_editor editor_mode
  25. end
  26. 1 def adapt_reference_editor_for_images
  27. nest_name = card.autoname(card.name.field("image01"))
  28. voo.show! :content_tab
  29. @nest_content_tab = nest(nest_name, view: :new_image, type: :image, hide: :guide)
  30. image_name = nest_name.to_name.right
  31. @nest_snippet = Card::Reference::NestParser.new_image image_name
  32. end
  33. 1 def new_image_form_opts
  34. { buttons: new_image_buttons,
  35. success: { tinymce_id: Env.params[:tinymce_id],
  36. view: :open } }
  37. end
  38. 1 def new_image_buttons
  39. button_formgroup do
  40. [standard_save_button(no_origin_update: true, class: "_change-create-to-update")]
  41. end
  42. end
  43. end
  44. 2 module JsFormat; module_parent.send :register_set_format, Card::Format::JsFormat, self; extend Card::Set::AbstractFormat
  45. 1 view :change_create_to_update, unknown: true do
  46. tm_id = if Env.params[:tinymce_id].present?
  47. "\"#{Env.params[:tinymce_id]}\""
  48. else
  49. '$(".tinymce-textarea").attr("id")'
  50. end
  51. <<-JAVASCRIPT.strip_heredoc
  52. nest.changeCreateToUpdate(#{tm_id});
  53. JAVASCRIPT
  54. end
  55. 1 view :open_nest_editor, unknown: true do
  56. tm_id = if Env.params[:tinymce_id].present?
  57. "\"#{Env.params[:tinymce_id]}\""
  58. else
  59. '$(".tinymce-textarea").attr("id")'
  60. end
  61. <<-JAVASCRIPT.strip_heredoc
  62. tm = tinymce.get(#{tm_id});
  63. nest.insertNest(tm, "{{+#{card.name.tag}|view: content; size: medium}}");
  64. JAVASCRIPT
  65. end
  66. end
  67. end;end;end;end;end;
  68. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/reference_editor/nest_image.rb ~~

card/tmpsets/set/mod028-card-mod-tinymce_editor/all/tinymce_editor.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (TinymceEditor)
  4. #
  5. 1 module TinymceEditor;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/tinymce_editor.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 def tinymce_editor_input
  10. 56 text_area :content, rows: 3, class: "tinymce-textarea d0-card-content",
  11. id: unique_id
  12. end
  13. end
  14. end;end;end;end;
  15. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/all/tinymce_editor.rb ~~

card/tmpsets/set/mod028-card-mod-tinymce_editor/self/script_tinymce.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptTinymce"
  4. #
  5. 1 module ScriptTinymce;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/script_tinymce.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptMods.add_item :script_tinymce
  10. 1 Self::InputOptions.add_to_basket :options, "tinymce editor"
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/script_tinymce.rb ~~

card/tmpsets/set/mod028-card-mod-tinymce_editor/self/script_tinymce_config.rb

100.0% lines covered

7 relevant lines. 7 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "ScriptTinymceConfig"
  4. #
  5. 1 module ScriptTinymceConfig;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/script_tinymce_config.rb"; end
  8. 1 include_set Abstract::CodeFile
  9. 1 Self::ScriptEditors.add_item :script_tinymce_config
  10. 1 All::Head::HtmlFormat.add_to_basket :mod_js_config, [:tiny_mce, "setTinyMCEConfig"]
  11. end;end;end;end;
  12. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/script_tinymce_config.rb ~~

card/tmpsets/set/mod028-card-mod-tinymce_editor/self/tiny_mce.rb

83.33% lines covered

6 relevant lines. 5 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Self
  3. # Set: The card "TinyMce"
  4. #
  5. 1 module TinyMce;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/tiny_mce.rb"; end
  8. 1 def raw_help_text
  9. <<-TEXT
  10. Configure [[http://tinymce.com|TinyMCE]], Decko's default
  11. [[http://en.wikipedia.org/wiki/Wysiwyg|wysiwyg]] editor.
  12. [[http://decko.org/TinyMCE|more]]
  13. TEXT
  14. end
  15. end;end;end;end;
  16. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-tinymce_editor/set/self/tiny_mce.rb ~~

card/tmpsets/set/mod030-card-mod-monkey/all/event_viz.rb

33.33% lines covered

36 relevant lines. 12 lines covered and 24 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (EventViz)
  4. #
  5. # the events method is a developer's tool for visualizing the event order
  6. # for a given card.
  7. # For example, from a console you might run
  8. #
  9. # puts mycard.events :update
  10. #
  11. # to see the order of events that will be executed on mycard.
  12. 1 module EventViz;
  13. 1 extend Card::Set
  14. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-monkey/set/all/event_viz.rb"; end
  15. # The indention and arrows (^v) indicate event dependencies.
  16. #
  17. # Note: as of yet, the functionality is a bit rough. It does not display events
  18. # that are called directly from within other events,
  19. # and certain event requirements (like the presence of an 'act') may
  20. # prevent events from showing up in the tree.
  21. 1 def events action
  22. @action = action
  23. events = Director::Stages::STAGES.map { |stage| events_tree "#{stage}_stage" }
  24. @action = nil
  25. print_events events
  26. end
  27. 1 def events_tree filt
  28. try("_#{filt}_callbacks")&.each_with_object({ name: filt }) do |callback, hash|
  29. events_branch hash, callback.kind, callback.filter if callback.applies? self
  30. end
  31. end
  32. 1 private
  33. 1 def print_events events, prefix="", depth=0
  34. depth += 1
  35. space = " " * (depth * 2)
  36. text = ""
  37. events.each do |event|
  38. text += print_event_pre event, depth, space
  39. text += print_event_main event, prefix
  40. text += print_event_post event, depth, space
  41. end
  42. text
  43. end
  44. 1 def print_event_pre event, depth, space
  45. if event[:before]
  46. print_events event[:before], space + "v ", depth
  47. elsif event[:around]
  48. print_events event[:around], space + "vv ", depth
  49. else
  50. ""
  51. end
  52. end
  53. 1 def print_event_main event, prefix
  54. "#{prefix}#{event[:name]}\n"
  55. end
  56. 1 def print_event_post event, depth, space
  57. return "" unless event[:after]
  58. print_events event[:after], space + "^ ", depth
  59. end
  60. 1 def events_branch hash, kind, filter
  61. hash[kind] ||= []
  62. hash[kind] << events_tree(filter)
  63. end
  64. end;end;end;end;
  65. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-monkey/set/all/event_viz.rb ~~

card/tmpsets/set/mod030-card-mod-monkey/all/view_viz.rb

38.89% lines covered

18 relevant lines. 7 lines covered and 11 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class All
  3. # Set: All cards (ViewViz)
  4. #
  5. 1 module ViewViz;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-monkey/set/all/view_viz.rb"; end
  8. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  9. 1 view :views_by_format do
  10. format_views =
  11. self.class.ancestors.each_with_object({}) do |format_class, hash|
  12. views =
  13. format_class.instance_methods.map do |method|
  14. next unless method.to_s =~ /^_view_(.+)$/
  15. Regexp.last_match(1)
  16. end.compact
  17. next unless views.present?
  18. format_class.name =~ /^Card(::Set)?::(.+?)$/ #::(\w+Format)
  19. hash[Regexp.last_match(2)] = views
  20. end
  21. accordion_group format_views
  22. end
  23. 1 view :views_by_name do
  24. views = methods.map do |method|
  25. Regexp.last_match(1) if method.to_s.match?(/^_view_(.+)$/)
  26. end.compact.sort
  27. list_group views
  28. end
  29. end
  30. end;end;end;end;
  31. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-monkey/set/all/view_viz.rb ~~

card/tmpsets/set/mod030-card-mod-monkey/right/debug.rb

30.61% lines covered

49 relevant lines. 15 lines covered and 34 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 3 class Card; module Set; class Right
  3. # Set: All "+Debug" cards
  4. #
  5. 1 module Debug;
  6. 1 extend Card::Set
  7. 1 def self.source_location; "/Users/ethan/dev/decko/gem/card-mod-monkey/set/right/debug.rb"; end
  8. 1 def virtual?
  9. new?
  10. end
  11. 2 module HtmlFormat; module_parent.send :register_set_format, Card::Format::HtmlFormat, self; extend Card::Set::AbstractFormat
  12. 1 view :core do
  13. core_section_config(card.left).map do |item|
  14. section(*item)
  15. end
  16. end
  17. 1 def core_section_config subject
  18. [["Sets", tabs("set modules" => set_modules_accordion(subject),
  19. "all modules" => singleton_modules_list(subject),
  20. "patterns" => set_patterns_breadcrumb(subject))],
  21. ["Views", tabs("by format" => subformat(subject)._render_views_by_format,
  22. "by name" => subformat(subject)._render_views_by_name)],
  23. ["Events", tabs(create: "<pre>#{subject.events(:create)}</pre>",
  24. update: "<pre>#{subject.events(:update)}</pre>",
  25. delete: "<pre>#{subject.events(:delete)}</pre>")],
  26. ["Cache/DB Comparison", cache_comparison_table(subject)]]
  27. end
  28. # rubocop:disable AccessorMethodName
  29. 1 def set_modules_accordion subject
  30. sets = subject.set_modules.each_with_object({}) do |sm, hash|
  31. ans = sm.ancestors
  32. ans.shift
  33. hash[sm.to_s] = ans
  34. end
  35. accordion_group sets
  36. end
  37. 1 def set_patterns_breadcrumb subject
  38. links = subject.patterns.reverse.map { |pattern| link_to_card pattern.to_s }
  39. breadcrumb links
  40. end
  41. # rubocop:enable AccessorMethodName
  42. 1 def singleton_modules_list subject
  43. all_mods = subject.singleton_class.ancestors.map(&:to_s)
  44. all_mods.shift
  45. list_group all_mods
  46. end
  47. 1 def cache_comparison_table subject
  48. cache_card = Card.fetch(subject.key)
  49. db_card = Card.find_by_key(subject.key)
  50. return unless cache_card && db_card
  51. table(
  52. %i[name updated_at updater_id content inspect].map do |field|
  53. [field.to_s,
  54. h(cache_card.send(field)),
  55. h(db_card.send(field))]
  56. end,
  57. header: ["Field", "Cache Val", "Database Val"]
  58. )
  59. end
  60. 1 def section title, content
  61. %(
  62. <h2>#{title}</h2>
  63. #{content}
  64. )
  65. end
  66. 1 def class_locations klass
  67. methods = defined_methods(klass)
  68. file_groups = methods.group_by { |sl| sl[0] }
  69. file_counts = file_groups.map do |file, sls|
  70. lines = sls.map { |sl| sl[1] }
  71. count = lines.size
  72. line = lines.min
  73. { file: file, count: count, line: line }
  74. end
  75. file_counts.sort_by! { |fc| fc[:count] }
  76. file_counts.map { |fc| [fc[:file], fc[:line]] }
  77. end
  78. 1 def defined_methods klass
  79. methods =
  80. klass.methods(false).map { |m| klass.method(m) } +
  81. klass.instance_methods(false).map { |m| klass.instance_method(m) }
  82. methods.map!(&:source_location)
  83. methods.compact!
  84. methods
  85. end
  86. end
  87. end;end;end;end;
  88. # ~~ generated from /Users/ethan/dev/decko/gem/card-mod-monkey/set/right/debug.rb ~~

card/tmpsets/set_pattern/100-all.rb

81.82% lines covered

11 relevant lines. 9 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Set Pattern: All
  3. #
  4. 1 class Card::Set::All < Card::Set::Pattern::Base
  5. 1 extend Card::Set::Pattern::Helper
  6. 1 cattr_accessor :options
  7. 1 class << self
  8. 1 def label _name
  9. 140 "All cards"
  10. end
  11. 1 def short_label _name
  12. "everything"
  13. end
  14. 1 def prototype_args _anchor
  15. {}
  16. end
  17. end
  18. 1 register "All".underscore.to_sym, (options || {})
  19. end
  20. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/01_all.rb ~~

card/tmpsets/set_pattern/101-all_plus.rb

75.0% lines covered

12 relevant lines. 9 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Set Pattern: AllPlus
  3. #
  4. 1 class Card::Set::AllPlus < Card::Set::Pattern::Base
  5. 1 extend Card::Set::Pattern::Helper
  6. 1 cattr_accessor :options
  7. 1 class << self
  8. 1 @@options = { junction_only: true }
  9. 1 def label _name
  10. 'All "+" cards'
  11. end
  12. 1 def short_label _name
  13. 'all "+" cards'
  14. end
  15. 1 def prototype_args _anchor
  16. { name: "+" }
  17. end
  18. end
  19. 1 register "AllPlus".underscore.to_sym, (options || {})
  20. end
  21. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/02_all_plus.rb ~~

card/tmpsets/set_pattern/102-type.rb

95.0% lines covered

20 relevant lines. 19 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Set Pattern: Type
  3. #
  4. 1 class Card::Set::Type < Card::Set::Pattern::Base
  5. 1 extend Card::Set::Pattern::Helper
  6. 1 cattr_accessor :options
  7. 1 class << self
  8. 1 load "card/set/type.rb" # "load" not "require" so pattern reloads properly
  9. 1 def label name
  10. 53 %(All "#{name}" cards)
  11. end
  12. 1 def short_label name
  13. 14 %(all "#{name}s")
  14. end
  15. 1 def generic_label
  16. "cards of a given type"
  17. end
  18. 1 def prototype_args anchor
  19. 4 { type: anchor }
  20. end
  21. 1 def pattern_applies? card
  22. 7674 !!card.type_id
  23. end
  24. 1 def anchor_name card
  25. 6411 card.type_name
  26. end
  27. 1 def anchor_id card
  28. 6411 card.type_id
  29. end
  30. end
  31. 1 register "Type".underscore.to_sym, (options || {})
  32. end
  33. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/03_type.rb ~~

card/tmpsets/set_pattern/103-star.rb

76.92% lines covered

13 relevant lines. 10 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Set Pattern: Star
  3. #
  4. 1 class Card::Set::Star < Card::Set::Pattern::Base
  5. 1 extend Card::Set::Pattern::Helper
  6. 1 cattr_accessor :options
  7. 1 class << self
  8. 1 def label _name
  9. 'All "*" cards'
  10. end
  11. 1 def short_label _name
  12. 'all "*" cards'
  13. end
  14. 1 def prototype_args _anchor
  15. { name: "*dummy" }
  16. end
  17. 1 def pattern_applies? card
  18. 7674 card.name.star?
  19. end
  20. end
  21. 1 register "Star".underscore.to_sym, (options || {})
  22. end
  23. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/04_star.rb ~~

card/tmpsets/set_pattern/104-rstar.rb

85.71% lines covered

14 relevant lines. 12 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Set Pattern: Rstar
  3. #
  4. 1 class Card::Set::Rstar < Card::Set::Pattern::Base
  5. 1 extend Card::Set::Pattern::Helper
  6. 1 cattr_accessor :options
  7. 1 class << self
  8. 1 @@options = { junction_only: true }
  9. 1 def label _name
  10. 1 'All "+*" cards'
  11. end
  12. 1 def short_label _name
  13. 'all "+*" cards'
  14. end
  15. 1 def prototype_args _anchor
  16. { name: "*dummy+*dummy" }
  17. end
  18. 1 def pattern_applies? card
  19. 7674 card.name.rstar?
  20. end
  21. end
  22. 1 register "Rstar".underscore.to_sym, (options || {})
  23. end
  24. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/05_rstar.rb ~~

card/tmpsets/set_pattern/105-rule.rb

85.71% lines covered

14 relevant lines. 12 lines covered and 2 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Set Pattern: Rule
  3. #
  4. 1 class Card::Set::Rule < Card::Set::Pattern::Base
  5. 1 extend Card::Set::Pattern::Helper
  6. 1 cattr_accessor :options
  7. 1 class << self
  8. 1 @@options = { junction_only: true }
  9. 1 def label _name
  10. 11 "All rule cards"
  11. end
  12. 1 def short_label _name
  13. "all rule cards"
  14. end
  15. 1 def prototype_args _anchor
  16. { name: "*all+*create" }
  17. end
  18. 1 def pattern_applies? card
  19. 7674 card.is_rule?
  20. end
  21. end
  22. 1 register "Rule".underscore.to_sym, (options || {})
  23. end
  24. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/06_rule.rb ~~

card/tmpsets/set_pattern/106-right.rb

81.25% lines covered

16 relevant lines. 13 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Set Pattern: Right
  3. #
  4. 1 class Card::Set::Right < Card::Set::Pattern::Base
  5. 1 extend Card::Set::Pattern::Helper
  6. 1 cattr_accessor :options
  7. 1 class << self
  8. @@options = {
  9. 1 junction_only: true,
  10. assigns_type: true
  11. }
  12. 1 def label name
  13. 60 %(All "+#{name}" cards)
  14. end
  15. 1 def short_label name
  16. %(all "+#{name}s")
  17. end
  18. 1 def generic_label
  19. "given field cards"
  20. end
  21. 1 def prototype_args anchor
  22. { name: "*dummy+#{anchor}" }
  23. end
  24. 1 def anchor_name card
  25. 4380 card.name.tag
  26. end
  27. end
  28. 1 register "Right".underscore.to_sym, (options || {})
  29. end
  30. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/07_right.rb ~~

card/tmpsets/set_pattern/107-type_plus_right.rb

77.78% lines covered

18 relevant lines. 14 lines covered and 4 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Set Pattern: TypePlusRight
  3. #
  4. # Patterned field names on a specific type
  5. 1 class Card::Set::TypePlusRight < Card::Set::Pattern::Base
  6. 1 extend Card::Set::Pattern::Helper
  7. 1 cattr_accessor :options
  8. 1 class << self
  9. @@options = {
  10. 1 junction_only: true,
  11. assigns_type: true,
  12. anchor_parts_count: 2
  13. }
  14. 1 def label name
  15. 4 name = name.to_name
  16. 4 %(All "+#{name.tag}" cards on "#{name.left}" cards)
  17. end
  18. 1 def short_label name
  19. name = name.to_name
  20. %(all "+#{name.tag}" on "#{name.left}s")
  21. end
  22. 1 def generic_label
  23. "given field cards on a given type"
  24. end
  25. 1 def prototype_args anchor
  26. {
  27. name: "+#{anchor.tag}",
  28. supercard: Card.new(name: "*dummy", type: anchor.trunk_name)
  29. }
  30. end
  31. 1 def anchor_name card
  32. 4380 "#{left_type(card)}+#{card.name.tag}"
  33. end
  34. end
  35. 1 register "TypePlusRight".underscore.to_sym, (options || {})
  36. end
  37. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/08_type_plus_right.rb ~~

card/tmpsets/set_pattern/108-self.rb

94.12% lines covered

17 relevant lines. 16 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Set Pattern: Self
  3. #
  4. 1 class Card::Set::Self < Card::Set::Pattern::Base
  5. 1 extend Card::Set::Pattern::Helper
  6. 1 cattr_accessor :options
  7. 1 class << self
  8. 1 def label name
  9. 102 %(The card "#{name}")
  10. end
  11. 1 def short_label name
  12. 22 name
  13. end
  14. 1 def generic_label
  15. "a single card"
  16. end
  17. 1 def prototype_args anchor
  18. 4 { name: anchor }
  19. end
  20. 1 def anchor_name card
  21. 7674 card.name
  22. end
  23. 1 def anchor_id card
  24. 7674 card.id
  25. end
  26. end
  27. 1 register "Self".underscore.to_sym, (options || {})
  28. end
  29. # ~~ generated from /Users/ethan/dev/decko/gem/card/mod/core/set_pattern/09_self.rb ~~

cardname/lib/cardname.rb

90.24% lines covered

82 relevant lines. 74 lines covered and 8 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 require "active_support/inflector"
  3. 1 require "htmlentities"
  4. 1 class Cardname < String
  5. 1 require_relative "cardname/parts"
  6. 1 require_relative "cardname/variants"
  7. 1 require_relative "cardname/contextual"
  8. 1 require_relative "cardname/predicates"
  9. 1 require_relative "cardname/manipulate"
  10. 1 include Parts
  11. 1 include Variants
  12. 1 include Contextual
  13. 1 include Predicates
  14. 1 include Manipulate
  15. 1 OK4KEY_RE = '\p{Word}\*'
  16. 1 cattr_accessor :joint, :banned_array, :var_re, :uninflect, :params,
  17. :session, :stabilize
  18. 1 self.joint = "+"
  19. 1 self.banned_array = []
  20. 1 self.var_re = /\{([^\}]*\})\}/
  21. 1 self.uninflect = :singularize
  22. 1 self.stabilize = false
  23. 1 JOINT_RE = Regexp.escape joint
  24. 1 class << self
  25. 1 def cache
  26. 505226 @cache ||= {}
  27. end
  28. 1 def new obj
  29. 503657 return obj if obj.is_a? self.class
  30. 503657 str = stringify(obj)
  31. 503657 cached_name(str) || super(str)
  32. end
  33. 1 def cached_name str
  34. 503657 cache[str]
  35. end
  36. 1 def reset_cache str=nil
  37. 31 str ? cache.delete(str) : @cache = {}
  38. end
  39. 1 def stringify obj
  40. 503657 if obj.is_a?(Array)
  41. obj.map(&:to_s) * joint
  42. else
  43. 503657 obj.to_s
  44. end
  45. end
  46. 1 def nothing_banned?
  47. 137 return @nothing_banned unless @nothing_banned.nil?
  48. 1 @nothing_banned = banned_array.empty?
  49. end
  50. 1 def banned_re
  51. @banned_re ||= /[#{Regexp.escape((banned_array + [joint])).join}]/
  52. end
  53. # Sometimes the core rule "the key's key must be itself" (called "stable" below) is violated
  54. # eg. it fails with singularize as uninflect method for Matthias -> Matthia -> Matthium
  55. # Usually that means the name is a proper noun and not a plural.
  56. # You can choose between two solutions:
  57. # 1. don't uninflect if the uninflected key is not stable (stabilize = false)
  58. # 2. uninflect until the key is stable (stabilize = true)
  59. 1 def stable_key name
  60. 2865 key_one = name.send(uninflect)
  61. 2865 key_two = key_one.send(uninflect)
  62. 2865 return key_one unless key_one != key_two
  63. stabilize ? stable_key(key_two) : name
  64. end
  65. 1 def dangerous_methods
  66. 321 bang_methods = String.instance_methods.select { |m| m.to_s.ends_with?("!") }
  67. 1 %i[replace concat clear].concat bang_methods
  68. end
  69. 1 def split_parts str
  70. 215389 str.split(/\s*#{JOINT_RE}\s*/, -1)
  71. end
  72. end
  73. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  74. # ~~~~~~~~~~~~~~~~~~~~~~ INSTANCE ~~~~~~~~~~~~~~~~~~~~~~~~~
  75. 1 attr_reader :key
  76. 1 def initialize str
  77. 1538 self.class.cache[str] = super str.strip.encode("UTF-8")
  78. end
  79. 1 def s
  80. 334875 @s ||= String.new self
  81. end
  82. 1 alias_method :to_s, :s
  83. 1 alias_method :to_str, :s
  84. 1 def to_name
  85. 103858 self
  86. end
  87. 1 dangerous_methods.each do |m|
  88. 32 define_method m do |*args, &block|
  89. 31 reset
  90. 31 super(*args, &block)
  91. end
  92. end
  93. # dangerous, too
  94. 1 def []= index, val
  95. p = parts
  96. p[index] = val
  97. replace self.class.new(p)
  98. end
  99. 1 def << val
  100. replace self.class.new(parts << val)
  101. end
  102. 1 def key
  103. 164125 @key ||= part_keys.join(self.class.joint)
  104. end
  105. 1 def == other
  106. other_key =
  107. case
  108. 9926 when other.respond_to?(:key) then other.key
  109. 1092 when other.respond_to?(:to_name) then other.to_name.key
  110. else other.to_s
  111. end
  112. 5509 other_key == key
  113. end
  114. 1 private
  115. 1 def reset
  116. 31 self.class.reset_cache s
  117. 31 instance_variables.each do |var|
  118. 73 instance_variable_set var, nil
  119. end
  120. end
  121. end

cardname/lib/cardname/contextual.rb

82.43% lines covered

74 relevant lines. 61 lines covered and 13 lines missed.
    
  1. 1 class Cardname
  2. 1 module Contextual
  3. 1 RELATIVE_REGEXP = /\b_(left|right|whole|self|user|main|\d+|L*R?)\b/
  4. # @return true if name is left or right of context
  5. 1 def child_of? context
  6. return false unless junction?
  7. context_key = context.to_name.key
  8. absolute_name(context).parent_keys.include? context_key
  9. end
  10. 1 def relative?
  11. 423 starts_with_joint? || (s =~ RELATIVE_REGEXP).present?
  12. end
  13. 1 def simple_relative?
  14. starts_with_joint? && (s =~ RELATIVE_REGEXP).nil?
  15. end
  16. 1 def absolute?
  17. 147 !relative?
  18. end
  19. 1 def stripped
  20. 42 s.gsub RELATIVE_REGEXP, ""
  21. end
  22. 1 def starts_with_joint?
  23. 570 junction? && parts.first.empty?
  24. end
  25. 1 def from *from
  26. 1904 name_from(*from).s
  27. end
  28. # if possible, relativize name into one beginning with a "+". The new name must absolutize back to the correct
  29. # original name in the context of "from"
  30. 1 def name_from *from
  31. 1904 return self unless (remaining = remove_context *from)
  32. 336 compressed = remaining.compact.unshift(nil).to_name # exactly one nil at beginning
  33. 336 key == compressed.absolute_name(from).key ? compressed : self
  34. end
  35. 1 def remove_context *from
  36. 1904 return false unless from.compact.present?
  37. 546 remaining = parts_excluding *from
  38. 546 return false if remaining.compact.empty? || # all name parts in context
  39. remaining == parts # no name parts in context
  40. 336 remaining
  41. end
  42. 1 def parts_excluding *string
  43. 546 exclude_name = string.to_name
  44. 546 exclude_keys = exclude_name ? exclude_name.part_names.map(&:key) : []
  45. 546 parts_minus exclude_keys
  46. end
  47. 1 def parts_minus keys_to_ignore
  48. 546 parts.map do |part|
  49. 897 next if part.empty?
  50. 877 next if part =~ /^_/ # this removes relative parts. why?
  51. 877 next if keys_to_ignore.member? part.to_name.key
  52. 516 part
  53. end
  54. end
  55. 1 def absolute context
  56. 19499 context = (context || "").to_name
  57. 19499 new_parts = absolutize_contextual_parts context
  58. 19499 return "" if new_parts.empty?
  59. 19493 absolutize_extremes new_parts, context.s
  60. 19493 new_parts.join self.class.joint
  61. end
  62. 1 def absolute_name *args
  63. 13887 absolute(*args).to_name
  64. end
  65. 1 def nth_left n
  66. # 1 = left; 2= left of left; 3 = left of left of left....
  67. (n >= length ? parts[0] : parts[0..-n - 1]).to_name
  68. end
  69. 1 private
  70. 1 def absolutize_contextual_parts context
  71. 19499 parts.map do |part|
  72. 25963 case part
  73. 103 when /^_user$/i then user_part part
  74. 224 when /^_main$/i then self.class.params[:main_name]
  75. 486 when /^(_self|_whole|_)$/i then context.s
  76. 94 when /^_left$/i then context.trunk
  77. # note - inconsistent use of left v. trunk
  78. when /^_right$/i then context.tag
  79. when /^_(\d+)$/i then ordinal_part $~[1].to_i, context
  80. when /^_(L*)(R?)$/i then partmap_part $~, context
  81. 25056 else part
  82. end.to_s.strip
  83. end
  84. end
  85. 1 def user_part part
  86. 103 self.class.session || part
  87. end
  88. 1 def ordinal_part pos, context
  89. pos = context.length if pos > context.length
  90. context.parts[pos - 1]
  91. end
  92. 1 def partmap_part match, context
  93. l_s, r_s = match[1].size, !match[2].empty?
  94. l_part = context.nth_left l_s
  95. r_s ? l_part.tag : l_part.s
  96. end
  97. 1 def absolutize_extremes new_parts, context
  98. 19493 [0, -1].each do |i|
  99. 38986 next if new_parts[i].present?
  100. # following avoids recontextualizing with relative contexts.
  101. # Eg, `+A+B+.absolute('+A')` should be +A+B, not +A+A+B.
  102. 1853 next if new_parts.to_name.send "#{[ :start, :end ][i]}s_with_parts?", context
  103. 1685 new_parts[i] = context
  104. end
  105. end
  106. end
  107. end

cardname/lib/cardname/manipulate.rb

32.61% lines covered

46 relevant lines. 15 lines covered and 31 lines missed.
    
  1. 1 class Cardname
  2. 1 module Manipulate
  3. # swap a subname
  4. # keys are used for comparison
  5. 1 def swap old, new
  6. old_name = old.to_name
  7. new_name = new.to_name
  8. return self if old_name.num_parts > num_parts
  9. return swap_part(old_name, new_name) if old_name.simple?
  10. return self unless include? old_name
  11. swap_all_subsequences(old_name, new_name).to_name
  12. end
  13. 1 def swap_part oldpart, newpart
  14. ensure_simpleness oldpart, "Use 'swap' to swap junctions"
  15. oldpart = oldpart.to_name
  16. newpart = newpart.to_name
  17. parts.map do |p|
  18. oldpart == p ? newpart : p
  19. end.to_name
  20. end
  21. 1 def swap_piece oldpiece, newpiece
  22. oldpiece = oldpiece.to_name
  23. newpiece = newpiece.to_name
  24. return swap_part oldpiece, newpiece if oldpiece.simple?
  25. return self unless starts_with_parts?(oldpiece)
  26. return newpiece if oldpiece.num_parts == num_parts
  27. self.class.new [newpiece, self[oldpiece.num_parts..-1]].flatten
  28. end
  29. 1 def num_parts
  30. 1853 parts.length
  31. end
  32. 1 def prepend_joint
  33. 7 joint = self.class.joint
  34. 7 self =~ /^#{Regexp.escape joint}/ ? self : (joint + self)
  35. end
  36. 1 def sub_in str, with:
  37. %i[capitalize downcase].product(%i[pluralize singularize])
  38. .inject(str) do |s, (m1, m2)|
  39. s.gsub(/\b#{send(m1).send(m2)}\b/, with.send(m1).send(m2))
  40. end
  41. end
  42. 1 alias_method :to_field, :prepend_joint
  43. 1 private
  44. 1 def swap_all_subsequences oldseq, newseq
  45. res = []
  46. i = 0
  47. while i <= num_parts - oldseq.num_parts
  48. # for performance reasons: check first character first then the rest
  49. if oldseq.part_keys.first == part_keys[i] &&
  50. oldseq.part_keys == part_keys[i, oldseq.num_parts]
  51. res += newseq.parts
  52. i += oldseq.num_parts
  53. else
  54. res << parts[i]
  55. i += 1
  56. end
  57. end
  58. res += parts[i..-1] if i < num_parts
  59. res
  60. end
  61. 1 def ensure_simpleness part, msg=nil
  62. return if part.to_name.simple?
  63. raise StandardError, "'#{part}' has to be simple. #{msg}"
  64. end
  65. end
  66. end

cardname/lib/cardname/parts.rb

72.41% lines covered

58 relevant lines. 42 lines covered and 16 lines missed.
    
  1. 1 class Cardname
  2. # naming conventions:
  3. # methods that end with _name return name objects
  4. # the same methods without _name return strings
  5. 1 module Parts
  6. 1 attr_reader :parts, :part_keys, :simple
  7. 1 alias_method :to_a, :parts
  8. 1 def parts
  9. 145114 @parts = Cardname.split_parts s
  10. end
  11. 1 def simple
  12. 101122 @simple = parts.size <= 1
  13. end
  14. 1 alias simple? simple
  15. # @return true if name has more than one part
  16. 1 def compound?
  17. 28425 !simple?
  18. end
  19. 1 alias junction? compound?
  20. 1 def part_keys
  21. 2786 @part_keys ||= simple ? [simple_key] : parts.map { |p| p.to_name.simple_key }
  22. end
  23. 1 def left
  24. 4118 @left ||= simple? ? nil : parts[0..-2] * self.class.joint
  25. end
  26. 1 def right
  27. 20982 @right ||= simple? ? nil : parts[-1]
  28. end
  29. 1 def left_name
  30. 15482 @left_name ||= left && self.class.new(left)
  31. end
  32. 1 def right_name
  33. 10810 @right_name ||= right && self.class.new(right)
  34. end
  35. 1 def left_key
  36. @left_key ||= simple? ? nil : part_keys[0..-2] * self.class.joint
  37. end
  38. 1 def right_key
  39. @right_key ||= simple? ? nil : part_keys.last
  40. end
  41. 1 def parents
  42. @parents ||= junction? ? [left, right] : []
  43. end
  44. 1 def parent_names
  45. @parent_names ||= junction? ? [left_name, right_name] : []
  46. end
  47. 1 def parent_keys
  48. @parent_keys ||= junction? ? [left_key, right_key] : []
  49. end
  50. # Note that all names have a trunk and tag,
  51. # but only junctions have left and right
  52. 1 def trunk
  53. 94 @trunk ||= simple? ? s : left
  54. end
  55. 1 def tag
  56. 8940 @tag ||= simple? ? s : right
  57. end
  58. 1 def trunk_name
  59. 1495 @trunk_name ||= simple? ? self : left_name
  60. end
  61. 1 def tag_name
  62. 712 @tag_name ||= simple? ? self : right_name
  63. end
  64. 1 def part_names
  65. 4006 @part_names ||= parts.map(&:to_name)
  66. end
  67. 1 def piece_names
  68. @piece_names ||= pieces.map(&:to_name)
  69. end
  70. # self and all ancestors (= parts and recursive lefts)
  71. # @example
  72. # "A+B+C+D".to_name.pieces
  73. # # => ["A", "B", "C", "D", "A+B", "A+B+C", "A+B+C+D"]
  74. 1 def pieces
  75. @pieces ||=
  76. if simple?
  77. [self]
  78. else
  79. junction_pieces = []
  80. parts[1..-1].inject parts[0] do |left, right|
  81. piece = [left, right] * self.class.joint
  82. junction_pieces << piece
  83. piece
  84. end
  85. parts + junction_pieces
  86. end
  87. end
  88. 1 def ancestors
  89. @ancestors ||= pieces.reject { |p| p == self}
  90. end
  91. # def + other
  92. # self.class.new(parts + other.to_name.parts)
  93. # end
  94. 1 def [] *args
  95. 1853 self.class.new parts[*args]
  96. end
  97. # full support of array methods caused trouble with `flatten` calls
  98. # It splits the parts of names in arrays
  99. # # name parts can be accessed and manipulated like an array
  100. # def method_missing method, *args, &block
  101. # if ARRAY_METHODS.include? method # parts.respond_to?(method)
  102. # self.class.new parts.send(method, *args, &block)
  103. # else
  104. # super
  105. # end
  106. # end
  107. #
  108. # def respond_to? method, include_private=false
  109. # return true if ARRAY_METHODS.include? method
  110. # super || parts.respond_to?(method, include_private)
  111. # end
  112. end
  113. end

cardname/lib/cardname/predicates.rb

76.47% lines covered

17 relevant lines. 13 lines covered and 4 lines missed.
    
  1. 1 class Cardname
  2. 1 module Predicates
  3. 1 def valid?
  4. 137 return true if self.class.nothing_banned?
  5. !parts.find do |pt|
  6. pt.match self.class.banned_re
  7. end
  8. end
  9. # @return true if name starts with the same parts as `prefix`
  10. 1 def starts_with_parts? *prefix
  11. 1835 start_name = prefix.to_name
  12. 1835 start_name == self[0, start_name.num_parts]
  13. end
  14. 1 alias_method :start_with_parts?, :starts_with_parts?
  15. # @return true if name ends with the same parts as `prefix`
  16. 1 def ends_with_parts? *suffix
  17. 18 end_name = suffix.to_name
  18. 18 end_name == self[-end_name.num_parts..-1]
  19. end
  20. 1 alias_method :end_with_parts?, :ends_with_parts?
  21. # @return true if name has a chain of parts that equals `subname`
  22. 1 def include? subname
  23. subkey = subname.to_name.key
  24. key =~ /(^|#{JOINT_RE})#{Regexp.quote subkey}($|#{JOINT_RE})/
  25. end
  26. end
  27. end

cardname/lib/cardname/variants.rb

100.0% lines covered

16 relevant lines. 16 lines covered and 0 lines missed.
    
  1. 1 class Cardname
  2. 1 module Variants
  3. 1 def simple_key
  4. 2094 return "" if empty?
  5. 2050 decoded
  6. .underscore
  7. .gsub(/[^#{OK4KEY_RE}]+/, '_')
  8. .split(/_+/)
  9. .reject(&:empty?)
  10. 2865 .map { |key| self.class.stable_key(key) }
  11. .join('_')
  12. end
  13. 1 def url_key
  14. 10202 @url_key ||= part_names.map do |part_name|
  15. 358 stripped = part_name.decoded.gsub(/[^#{OK4KEY_RE}]+/, ' ').strip
  16. 358 stripped.gsub(/[\s\_]+/, '_')
  17. end * self.class.joint
  18. end
  19. # safe to be used in HTML as id ('*' and '+' are not allowed),
  20. # but the key is no longer unique.
  21. # For example "A-XB" and "A+*B" have the same safe_key
  22. 1 def safe_key
  23. 6169 @safe_key ||= key.tr('*', 'X').tr self.class.joint, '-'
  24. end
  25. 1 def decoded
  26. 2408 @decoded ||= s.index('&') ? HTMLEntities.new.decode(s) : s
  27. end
  28. 1 def to_sym
  29. 21 s.to_sym
  30. end
  31. end
  32. end

decko-cucumber/lib/decko/cucumber.rb

100.0% lines covered

9 relevant lines. 9 lines covered and 0 lines missed.
    
  1. 1 require "rspec"
  2. 1 require "capybara-puma"
  3. 1 require "selenium-webdriver"
  4. 1 require "chromedriver-helper"
  5. 1 require "capybara"
  6. 1 require "database_cleaner"
  7. 1 require "launchy"
  8. 1 require "cucumber"
  9. 1 require "email_spec"

decko-spring/lib/decko/spring.rb

100.0% lines covered

3 relevant lines. 3 lines covered and 0 lines missed.
    
  1. 1 require "listen"
  2. # require "spring"
  3. 1 require "spring-commands-rspec"
  4. 1 require "spring-commands-cucumber"

decko/lib/decko.rb

92.31% lines covered

13 relevant lines. 12 lines covered and 1 lines missed.
    
  1. 1 module Decko
  2. 1 DECKO_GEM_ROOT = File.expand_path("../..", __FILE__)
  3. 1 class << self
  4. 1 def root
  5. Rails.root
  6. end
  7. 1 def application
  8. 8 Rails.application
  9. end
  10. 1 def config
  11. 2 application.config
  12. end
  13. 1 def paths
  14. 4 application.paths
  15. end
  16. 1 def gem_root
  17. 13 DECKO_GEM_ROOT
  18. end
  19. end
  20. end

decko/lib/decko/application.rb

97.62% lines covered

42 relevant lines. 41 lines covered and 1 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 require "decko/engine"
  3. 1 require_relative "config/initializers/sedate_parser"
  4. 1 Bundler.require :default, *Rails.groups
  5. 1 module Decko
  6. 1 class Application < Rails::Application
  7. 1 initializer :load_decko_environment_config,
  8. before: :load_environment_config, group: :all do
  9. 1 add_path paths, "lib/decko/config/environments", glob: "#{Rails.env}.rb"
  10. 1 paths["lib/decko/config/environments"].existent.each do |environment|
  11. 1 require environment
  12. end
  13. end
  14. 1 class << self
  15. 1 def inherited base
  16. 1 super
  17. 1 Rails.app_class = base
  18. 1 add_lib_to_load_path!(find_root(base.called_from))
  19. 1 ActiveSupport.run_load_hooks(:before_configuration, base.instance)
  20. end
  21. end
  22. 1 def add_path paths, path, options={}
  23. 1 root = options.delete(:root) || Decko.gem_root
  24. 1 options[:with] = File.join(root, (options[:with] || path))
  25. 1 paths.add path, options
  26. end
  27. 1 def config
  28. 1239 @config ||= begin
  29. 1 config = super
  30. 1 Cardio.set_config config
  31. # any config settings below:
  32. # (a) do not apply to Card used outside of a Decko context
  33. # (b) cannot be overridden in a deck's application.rb, but
  34. # (c) CAN be overridden in an environment file
  35. # therefore, in general, they should be restricted to settings that
  36. # (1) are specific to the web environment, and
  37. # (2) should not be overridden
  38. # ..and we should address (c) above!
  39. # general card settings (overridable and not) should be in cardio.rb
  40. # overridable decko-specific settings don't have a place yet
  41. # but should probably follow the cardio pattern.
  42. # config.load_defaults "6.0"
  43. 1 config.autoloader = :zeitwerk
  44. 1 config.load_default = "6.0"
  45. 1 config.i18n.enforce_available_locales = true
  46. # config.active_record.raise_in_transactional_callbacks = true
  47. 1 config.allow_concurrency = false
  48. 1 config.assets.enabled = false
  49. 1 config.assets.version = "1.0"
  50. 1 config.filter_parameters += [:password]
  51. # Rails.autoloaders.log!
  52. 1 Rails.autoloaders.main.ignore(File.join(Cardio.gem_root, "lib/card/seed_consts.rb"))
  53. 1 config
  54. end
  55. end
  56. 1 def paths
  57. 19 @paths ||= begin
  58. 1 paths = super
  59. 1 Cardio.set_paths paths
  60. 1 paths.add "files"
  61. 1 paths["app/models"] = []
  62. 1 paths["app/mailers"] = []
  63. 1 unless paths["config/routes.rb"].existent.present?
  64. add_path paths, "config/routes.rb",
  65. with: "rails/application-routes.rb"
  66. end
  67. 1 paths
  68. end
  69. end
  70. end
  71. end

decko/lib/decko/engine.rb

100.0% lines covered

29 relevant lines. 29 lines covered and 0 lines missed.
    
  1. 1 require "rails/all"
  2. 1 require "cardio"
  3. # TODO: Move these to modules that use them
  4. 1 require "htmlentities"
  5. 1 require "coderay"
  6. 1 require "haml"
  7. 1 require "kaminari"
  8. 1 require "bootstrap4-kaminari-views"
  9. 1 require "diff/lcs"
  10. 1 require "builder"
  11. 1 require "decko"
  12. 1 module Decko
  13. 1 class Engine < ::Rails::Engine
  14. 1 paths.add "app/controllers", with: "rails/controllers", eager_load: true
  15. 1 paths.add "gem-assets", with: "rails/assets"
  16. 1 paths.add "config/routes.rb", with: "rails/engine-routes.rb"
  17. 1 paths.add "lib/tasks", with: "#{::Decko.gem_root}/lib/decko/tasks",
  18. glob: "**/*.rake"
  19. 1 paths["lib/tasks"] << "#{::Cardio.gem_root}/lib/card/tasks"
  20. 1 paths.add "lib/decko/config/initializers",
  21. with: File.join(Decko.gem_root, "lib/decko/config/initializers"),
  22. glob: "**/*.rb"
  23. 1 initializer "decko.engine.load_config_initializers",
  24. after: :load_config_initializers do
  25. 1 paths["lib/decko/config/initializers"].existent.sort.each do |initializer|
  26. 3 load(initializer)
  27. end
  28. end
  29. 1 initializer "engine.copy_configs",
  30. before: "decko.engine.load_config_initializers" do
  31. # this code should all be in Decko somewhere, and it is now, gem-wize
  32. # Ideally railties would do this for us; this is needed for both use cases
  33. 1 Engine.paths["request_log"] = Decko.paths["request_log"]
  34. 1 Engine.paths["log"] = Decko.paths["log"]
  35. 1 Engine.paths["lib/tasks"] = Decko.paths["lib/tasks"]
  36. 1 Engine.paths["config/routes.rb"] = Decko.paths["config/routes.rb"]
  37. end
  38. 1 initializer :connect_on_load do
  39. 1 ActiveSupport.on_load(:active_record) do
  40. 1 ActiveRecord::Base.establish_connection(::Rails.env.to_sym)
  41. end
  42. # ActiveSupport.on_load(:after_initialize) do
  43. # # require "card" if Cardio.load_card?
  44. # Card if Cardio.load_card?
  45. # rescue ActiveRecord::StatementInvalid => e
  46. # ::Rails.logger.warn "database not available[#{::Rails.env}] #{e}"
  47. # end
  48. end
  49. end
  50. end

decko/lib/decko/response.rb

87.5% lines covered

88 relevant lines. 77 lines covered and 11 lines missed.
    
  1. 1 module Decko
  2. # methods for managing decko responses
  3. 1 module Response
  4. 1 def response_format
  5. 315 @response_format ||= format_name_from_params
  6. end
  7. 1 private
  8. 1 def respond format, result, status
  9. 315 if status.in? [302, 303]
  10. hard_redirect result
  11. 315 elsif format.is_a?(Card::Format::FileFormat) && status == 200
  12. 30 send_file(*result)
  13. else
  14. 285 render_response result.to_s.html_safe, status, format.mime_type
  15. end
  16. end
  17. 1 def render_response body, status, content_type
  18. 285 render body: body, status: status, content_type: content_type
  19. end
  20. 1 def redirect_cud_success success
  21. 157 redirect_type = success.redirect || default_cud_success_redirect_type
  22. 157 if redirect_type.to_s == "soft"
  23. 28 success.target ||= self
  24. 28 soft_redirect success
  25. else
  26. 129 hard_redirect success.to_url, 303
  27. end
  28. end
  29. 1 def default_cud_success_redirect_type
  30. 107 Card::Env.ajax? ? "soft" : "hard"
  31. end
  32. # return a redirect response
  33. 1 def hard_redirect url, status=302
  34. 129 url = card_url url # make sure we have absolute url
  35. 129 if Card::Env.ajax?
  36. # lets client reset window location
  37. # (not just receive redirected response)
  38. # formerly used 303 response, but that gave IE the fits
  39. 50 render json: { redirect: url }
  40. else
  41. 79 redirect_to url, status: status
  42. end
  43. end
  44. # return a standard GET response directly.
  45. # Used in AJAX situations where PRG pattern is unwieldy
  46. 1 def soft_redirect success
  47. # Card::Cache.renew
  48. 28 @card = success.target
  49. 28 require_card_for_soft_redirect!
  50. 28 self.params = Card::Env[:params] = soft_redirect_params
  51. 28 load_action
  52. 28 show
  53. end
  54. 1 def reload
  55. render json: { reload: true }
  56. end
  57. 1 def soft_redirect_params
  58. 28 new_params = params.clone
  59. 28 new_params.delete :card
  60. 28 new_params.delete :action
  61. 28 new_params.merge Card::Env.success.params
  62. end
  63. 1 def require_card_for_soft_redirect!
  64. 28 return if card.is_a? Card
  65. raise Card::Error, "tried to do soft redirect without a card"
  66. end
  67. # (obviously) deprecated
  68. 1 def send_deprecated_asset
  69. filename = [params[:mark], params[:format]].compact.join(".")
  70. send_file asset_file_path(filename), x_sendfile: true
  71. end
  72. 1 def asset_file_path filename
  73. path = Decko::Engine.paths["gem-assets"].existent.first
  74. path = File.join path, filename
  75. validate_path filename, path
  76. path
  77. end
  78. 1 def validate_path filename, path
  79. # for security, block relative paths
  80. raise Card::Error::BadAddress if filename.include?("../") || !::File.exist?(path)
  81. end
  82. # TODO: everything below should go in a separate file
  83. # below is about beginning (initialization). above is about end (response)
  84. # Both this file and that would make sense as submodules of CardController
  85. 1 def load_format
  86. 315 request.format = :html if implicit_html?
  87. 315 card.format response_format
  88. end
  89. 1 def implicit_html?
  90. 315 (Card::Env.ajax? && !params[:format]) || request.format.to_s == "*/*"
  91. end
  92. 1 def format_name_from_params
  93. 315 if explicit_file_format? then :file
  94. 285 elsif params[:format].present? then params[:format].to_sym
  95. 274 else request.format.to_sym
  96. end
  97. end
  98. 1 def explicit_file_format?
  99. 315 params[:explicit_file] || !Card::Format.registered.member?(request.format)
  100. end
  101. 1 def interpret_mark mark
  102. 284 case mark
  103. when "*previous"
  104. # Why support this? It's only needed in Success, right? Deprecate?
  105. return hard_redirect(Card::Env.previous_location)
  106. when nil
  107. 71 implicit_mark
  108. else
  109. 213 explicit_mark mark
  110. end
  111. end
  112. 1 def explicit_mark mark
  113. # we should find the place where we produce these bad urls
  114. 213 mark.valid_encoding? ? mark : mark.force_encoding("ISO-8859-1").encode("UTF-8")
  115. end
  116. 1 def implicit_mark
  117. case
  118. 72 when initial_setup then ""
  119. 43 when (name = params.dig :card, :name) then name
  120. 11 when view_does_not_require_name? then ""
  121. 16 else home_mark
  122. end
  123. end
  124. 1 def home_mark
  125. 16 Card::Rule.global_setting(:home) || "Home"
  126. end
  127. 1 def view_does_not_require_name?
  128. 27 return false unless (view = params[:view]&.to_sym)
  129. 11 Card::Set::Format::AbstractFormat::ViewOpts.unknown_ok[view]
  130. end
  131. # alters params
  132. 1 def initial_setup
  133. 71 return unless initial_setup?
  134. 1 prepare_setup_card!
  135. end
  136. 1 def initial_setup?
  137. 71 Card::Auth.needs_setup? && Card::Env.html?
  138. end
  139. 1 def prepare_setup_card!
  140. 1 params[:card] = { type_id: Card.default_accounted_type_id }
  141. 1 params[:view] = "setup"
  142. end
  143. end
  144. end

decko/rails/controllers/application_controller.rb

100.0% lines covered

1 relevant lines. 1 lines covered and 0 lines missed.
    
  1. 1 class ApplicationController < ActionController::Base
  2. end

decko/rails/controllers/card_controller.rb

96.15% lines covered

78 relevant lines. 75 lines covered and 3 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. # Decko's only controller.
  3. 1 class CardController < ApplicationController
  4. 1 include ::Card::Env::Location
  5. 1 include ::Recaptcha::Verify
  6. 1 include ::Decko::Response
  7. 1 layout nil
  8. 1 attr_reader :card
  9. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  10. # PUBLIC METHODS
  11. 1 def create
  12. 112 handle { card.save! }
  13. end
  14. 1 def read
  15. 284 show
  16. end
  17. 1 def update
  18. 196 card.new_card? ? create : handle { card.update! params[:card] }
  19. end
  20. 1 def delete
  21. 14 handle { card.delete! }
  22. end
  23. # @deprecated
  24. 1 def asset
  25. Rails.logger.info "Routing assets through Card. Recommend symlink from " \
  26. 'Deck to Card gem using "rake decko:update_assets_symlink"'
  27. send_deprecated_asset
  28. end
  29. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  30. # PRIVATE METHODS
  31. 1 private
  32. #-------( FILTERS )
  33. 1 before_action :setup, except: [:asset]
  34. 1 before_action :authenticate, except: [:asset]
  35. 1 before_action :load_mark, only: [:read]
  36. 1 before_action :load_card, except: [:asset]
  37. 1 before_action :load_action, only: [:read]
  38. 1 before_action :refresh_card, only: [:create, :update, :delete]
  39. 1 def setup
  40. 444 Card::Machine.refresh_script_and_style unless params[:explicit_file]
  41. 444 Card::Cache.renew
  42. 444 Card::Env.reset controller: self
  43. end
  44. 1 def authenticate
  45. 444 Card::Auth.signin_with token: params[:token], api_key: params[:api_key]
  46. end
  47. 1 def load_mark
  48. 284 params[:mark] = interpret_mark params[:mark]
  49. end
  50. 1 def load_card
  51. 444 @card = Card.controller_fetch params
  52. 444 raise Card::Error::NotFound unless card
  53. 444 record_as_main
  54. end
  55. 1 def load_action
  56. 312 card.select_action_by_params params
  57. 312 card.content = card.last_draft_content if params[:edit_draft] && card.drafts.present?
  58. end
  59. # TODO: refactor this away this when new layout handling is ready
  60. 1 def record_as_main
  61. 444 Card::Env[:main_name] = params[:main] || card&.name || ""
  62. end
  63. 1 def refresh_card
  64. 160 @card = card.refresh
  65. end
  66. # ----------( HELPER METHODS ) -------------
  67. 1 def handle
  68. 160 Card::Env.success card.name
  69. 160 yield ? cud_success : raise(Card::Error::UserError)
  70. end
  71. # successful create, update, or delete act
  72. 1 def cud_success
  73. 157 success = Card::Env.success.in_context card.name
  74. 157 if success.reload?
  75. reload # instruct JSON to reload
  76. else
  77. 157 redirect_cud_success success
  78. end
  79. end
  80. 1 def show view=nil, status=200
  81. 315 card.action = :read
  82. 315 format = load_format
  83. 315 result = render_page format, view
  84. 315 status = format.error_status || status
  85. 315 respond format, result, status
  86. end
  87. 1 def render_page format, view
  88. 315 view ||= view_from_params
  89. 315 card.act do
  90. 315 format.page self, view, Card::Env.slot_opts
  91. end
  92. end
  93. 1 def view_from_params
  94. 312 params[:view] || params[:v]
  95. end
  96. 1 def handle_exception exception
  97. 3 raise exception if debug_exception?(exception)
  98. 3 @card ||= Card.new
  99. 3 error = Card::Error.report exception, card
  100. 3 show error.class.view, error.class.status_code
  101. end
  102. # TODO: move to exception object
  103. 1 def debug_exception? e
  104. 3 !e.is_a?(Card::Error::UserError) &&
  105. !e.is_a?(ActiveRecord::RecordInvalid) &&
  106. Card::Codename[:debugger] &&
  107. Card[:debugger]&.content =~ /on/ # && !Card::Env.ajax?
  108. end
  109. 1 class << self
  110. 1 def rescue_from_class *klasses
  111. 1 klasses.each do |klass|
  112. 8 rescue_from(klass) { |exception| handle_exception exception }
  113. end
  114. end
  115. 1 def rescue_all?
  116. 1 Cardio.config.rescue_all_in_controller
  117. end
  118. end
  119. 1 rescue_from_class(*Card::Error::UserError.user_error_classes)
  120. 1 rescue_from_class StandardError if rescue_all?
  121. end

decko/rails/engine-routes.rb

100.0% lines covered

27 relevant lines. 27 lines covered and 0 lines missed.
    
  1. # -*- encoding : utf-8 -*-
  2. 1 Decko::Engine.routes.draw do
  3. 1 files = Decko::Engine.config.files_web_path
  4. 1 file_matchers = { mark: /[^-]+/, explicit_file: true, rev_id: /[^-]+/ }
  5. 1 root "card#read"
  6. # explicit file request
  7. 1 get({ "#{files}/:mark/:rev_id(-:size).:format" => "card#read" }.merge(file_matchers))
  8. # DEPRECATED (old file and asset requests)
  9. 1 get({ "#{files}/:mark(-:size)-:rev_id.:format" => "card#read" }.merge(file_matchers))
  10. 1 %w[assets javascripts jasmine].each do |prefix|
  11. 3 get "#{prefix}/*mark" => "card#asset"
  12. end
  13. # Standard GET requests
  14. 1 get "(/decko)/:mark(.:format)" => "card#read" # /decko is deprecated
  15. # Alternate GET requests
  16. 1 get "new/:type" => "card#read", view: "new" # common case for card without mark
  17. 1 get "type/:type" => "card#read"
  18. 1 get ":mark/view/:view(.:format)" => "card#read" # simplifies API documentation
  19. 1 get "card/:view(/:mark(.:format))" => "card#read", view: /new|edit/ # legacy
  20. # RESTful (without mark)
  21. 1 post "/" => "card#create"
  22. 1 put "/" => "card#update"
  23. 1 patch "/" => "card#update"
  24. 1 delete "/" => "card#delete"
  25. # RESTful (with mark)
  26. 1 match ":mark(.:format)" => "card#create", via: :post
  27. 1 match ":mark(.:format)" => "card#update", via: %i[put patch]
  28. 1 match ":mark(.:format)" => "card#delete", via: :delete
  29. # explicit GET alternatives for transactions
  30. 1 %w[create read update delete asset].each do |action|
  31. 5 get "(card)/#{action}(/:mark(.:format))" => "card", action: action
  32. end
  33. # for super-explicit over-achievers
  34. 1 match "(card)/create(/:mark(.:format))" => "card#create", via: %i[post patch]
  35. 1 match "(card)/update(/:mark(.:format))" => "card#update", via: %i[post put patch]
  36. 1 match "(card)/delete(/:mark(.:format))" => "card#delete", via: :delete
  37. # for super-lazy under-achievers
  38. 1 get ":mark/:view(.:format)" => "card#read"
  39. # Wildcard for bad addresses
  40. 1 get "*mark" => "card#read", view: "bad_address"
  41. end